SPLat Knowledge Base

The SPLat Knowledge Base (SKB) is a comprehensive documentation system covering the SPLat product. It is published online in HTML format. The HTML help file is distributed with our programming software, SPLat/PC. It can also be generated as a printable Microsoft Word document, on special request (in general we avoid hard copy because the content changes frequently), but beware, it is well over 1,000 pages in that form, and the formatting is imperfect.

The SKB consists of a collection of documents covering the various aspects of SPLat. These documents range from individual short articles on technical topics of interest, through to full technical manuals on individual SPLat products. The SKB brings them all together as one large structured document with a shared index and glossary.

The SKB is not a static document. It is constantly being added to and revised as the SPLat product evolves. If you received this on CD or as a single download, you should check back regularly with the online version when you are researching any particular topic. You will surely even find some topic headings with nothing in them. That means they are planned for future versions of the SKB. The online version will generally be the most up to date.

Because we change the SKB frequently, we do not attempt to track revisions.

Trade marks

SPLat® and the SPLat logo are a registered trademark of Microconsultants Pty Ltd.

EasyOne™, EasyStep™, SimpleHMI™, Tabula™, Active Template™, Xwire™, Tiny Serial Peripheral™, FastTrack™, MultiTrack™, MultiTrack+™, Touch of SPice™, SPice™, SPx™, SPiceX™, XPice™, SPLatLink™, SLCom™, reFlash™, PIDassist™ and SPLogger™ are trademarks of SPLat Controls Pty Ltd.

Microsoft Word, Microsoft Office, Windows®, Window 95™, Excel®, Window 98™, Windows XP™, Windows Vista™, Windows NT® and MS-DOS™ are trademarks of the Microsoft Corporation.

SitePlayer is a trademark of Netmedia, Inc.

ViSi-Genie is a trademark of 4D Systems Pty Ltd.

Next Topic


Product documentation

Previous Topic

Next Topic

In This Chapter

Product documentation: Controllers

Product documentation: Expansion boards

Product documentation: SPice add-on boards

Product documentation: Xwire peripherals

Product documentation: Bluetooth adaptors

Product documentation: RS485 adaptors

Product documentation: I/O adapter boards

Product documentation: Onboard peripherals


Product documentation: Controllers

A SPLat controller is any SPLat product that you program using the SPLat language. The controllers contained in this section are those released since early 2002. Older controllers are documented in the SPLat/PC help file. Specifically those are:

  • SP108
  • SL88
  • MMi88
  • AJ18
  • OEM32/36
  • PMC3

Previous Topic

Next Topic

In This Chapter

MS120 product documentation

CC18 product documentation

MMi203 product documentation

HD8 Product documentation

SL100 product documentation

MMi202 Product documentation

MMi201 Product documentation

MMi200 product documentation

MMi99 Product documentation

SL99 product documentation

CC16 product documentation


MS120 product documentation

Entry level pricing, up scale performance!

Congratulations on your choice of the SPLat MS120 controller. SPLat gives you the ability to implement a sophisticated control system in your product easily and inexpensively, and will give you an edge that will have your competitors wondering whatever hit them!

The SPLat MS120 controller gives you a feature set rivalling competing products costing many times as much. Although costing about the same as "programmable relays", entry level single board PLCs and hobbyist products, it offers considerably more capabilities.

  • Easier to program than C, Basic or ladder;
  • Built in operator interface with LCD display;
  • Can easily be "private labeled";
  • Large program memory (over 30K bytes, approx 18,000 instructions)(6K in the MS12);
  • Capable of sophisticated control functions including math, state machines, PID, algorithms, heuristics and lookup tables;
  • Built in multitasking;
  • Real-world I/O: 400mA outputs and ruggedised inputs;
  • Expandable: Extra I/O, special function add-ons (including analog) and custom functions.

Have your first working program in less than an hour!

Like all SPLat controllers, the MS120 is extremely easy to get started with. Our programming software is free, and includes a full simulator that allows you to test your program and explore possibilities without any hardware present. Our programming language has been skillfully crafted to make the simple tasks absurdly easy to program, and yet there is a rich repertoire of advanced instructions to satisfy the sophisticated user. Our training materials are legendary (and free). The MS120 contains real world input/output circuits, so you won't have to design your own.

How do we do it?

You may be sceptical about how we can deliver so much performance at such a budget price. When we design a controller we aim for the best possible value for money. We design and re-design our circuitry to eliminate unnecessary or expensive components. As far as possible we select multi-vendor components so we can shop around. Maybe most importantly, we are engineers designing for end users, not engineers designing for engineers. We don't allow ourselves to be carried away by megahertz and gigabytes. We understand that what is really important to an OEM operating in the real world is getting the necessary performance at a price that will give a competitive advantage.

One thing we are obsessive about is build quality. We hate returns, and get very few of them (way less than 1%). Every board we make is built with strict process controls. Every board is visually inspected. Every board is 100% functionally tested. And every board is backed by our 2-year standard product guarantee and Post-Nuke repair policy.

The MS120 supersedes the MS12. It has all the same features but higher specifications (faster, more program memory).

Previous Topic

Next Topic

In This Chapter

MS120: Introduction

MS120: Connections and adjustments

MS120: Power supply

MS120: External inputs and outputs

MS120: Operator interface

MS120: Non-volatile memory

MS120: Onboard counter type A (OBCA)

MS120: OnBoard Counter type B

MS120: OnBoard Quadrature Counter

MS120: CPU device

MS120: SPice connectors

MS120: Comms connector

MS120: Communications

MS120: Xwire

MS120: Programming

MS120: MultiTrack

MS120: Operating temperature ratings

MS120: Firmware revisions and reFlashing

MS120 specific accessories


MS120: Introduction

This document provides you with detailed technical information on the SPLat MS120 programmable controller.

This document is part of the SPLat Knowledge Base (SKB). The SKB is a comprehensive collection of documents covering all technical aspects of the SPLat product. It is published in electronic formats (HTML-Help and HTML) and is also available electronically as a series of document files you can print out yourself. By publishing the documents only in electronic form, we not only save some trees but are also able to make updates available to you very quickly on our website. We suggest you check our Website periodically to ensure you have the most recent version.

Previous Topic

Next Topic

In This Chapter

Getting started with your controller

MS120: Using this document

Other resources

MS120: Product overview

MS120: Differences from previous model


Getting started with your controller

You are possibly about to take your first step with a SPLat controller. We at SPLat Controls hope you find it a rewarding and fun process. We have worked very hard to make SPLat as easy to use, as versatile and as capable as possible. As part of that effort we have created a number of resources for you: tools, educational materials, documentation and more.

You will get the most out of all these materials if you give yourself time to at least get an overview of what is available.

The key resources are:

Mini Tutorials

The SPLat/PC programming software contains a number of "MiniTutes". These are very brief interactive tutorials that give you a quick overview of some aspect of SPLat. Each one takes 5-10 minutes. Even if you have never written a program before, the very first MiniTute, "Quick tour", will have you saying "Ahah!" within 15 minutes. The only skill you need to get started is how to use a computer. If you have got this far you probably have that.

Tutorials in the SKB

The SPLat Knowledge Base contains tutorials on a number of topics.

SPLat/PC programming software

This is the program you use to program your SPLat controller to do what you want it to do. This is a comprehensive program that provides program entry, simulation, downloading to the controller and more.

SPLat/PC is on the SPLat Resources CD, and you can also get the very latest version off our website at any time.

'Classic' help file

The help file that's part of SPLat/PC was the original electronic documentation for SPLat. It remains the only documentation for first generation SPLat controllers, and is still the principal source of reference material and knowledge on the basic internal workings of SPLat (registers etc.)

The classic help file installs with SPLat/PC. You can download the latest SPLat/PC here.

With the advent of Vista and Windows 7, Microsoft has dropped built-in support for its own classic help file format *.hlp. To be able to use the SPLat Classic help file on those systems you will need to download and install WinHlp32.exe. Click here for more information at Microsoft.

Previous Topic


MS120: Using this document

This document concerns itself primarily with information that is specific to the MS120 product. In particular, it concentrates on the hardware aspects of the MS120. Because programming is uniform across all SPLat products, detailed SPLat programming information is located elsewhere. For more introductory information on programming please see MS120 Programming within this document.

The material is organized largely according to various hardware features. This should allow you to quickly locate the information you need in order to properly match the MS120 to the outside world and make electrical connections. There are also a couple of chapters on more general topics like determining the permissible operating temperature.

We suggest you start with a quick read through the whole document to familiarize yourself with what's there. After that you should be able to use it as a technical reference to look up specific information you need.

Most technical product manuals contain a Specifications section. We've done it a bit differently (we do many things differently, which is probably why you are using SPLat!). The numbers and ratings are discussed as part of each feature. While this may seem to make the information a little harder to locate (compared to a consolidated table), it gives us the ability to loosen up a little and discuss how the operating envelope can be flexed under different conditions. For example, the allowable output current depends on the operating ambient temperature.


Other resources

On the CD that comes with each SPLat controller sold in a Developer's Kit version you will find a number of other useful files and resources. This includes training materials, drawings, program examples and other things. Spend a few minutes browsing the CD to familiarize yourself with what is available.


MS120: Product overview

The MS120 is a SPLat single board programmable logic controller that includes a number of innovative features. Key features are:

  • 8 bidirectional digital I/O points. Each can be used as an input or as an output.
  • 4 digital inputs which can be set to operate in NPN or PNP modes.
  • Onboard Operator Interface, with optional Liquid Crystal Display (LCD);
  • A versatile power supply scheme
  • An extremely flexible range of expansion possibilities.

The most unusual feature of the SPLat MS120 is its built in operator interface. This gives you push buttons, indicators and a liquid crystal display (optional) to allow interaction with an operator. It also gives you the ability to tailor the appearance of your end product via a plastic graphical membrane overlay.

The MS120 is supplied a several different packages.

  • MS120DK216. This is the Developer's Kit. It consists of the MS120 board plus programming software, programming cable and a set of matching connectors;
  • MS120OEM216. This is the "production" version of the board (only) with liquid crystal display;
  • MS120OEM. This is the "production" version of the board (only) without liquid crystal display.


MS120: Differences from previous model

MS120 versus MS12

  • The MS120 uses a newer, faster processor. It runs nearly 2.5 times faster (all timing instructions, of course, run at the correct speed).
  • The analog inputs and outputs are now 10-bit resolution. To take advantage of the added resolution you must use fAnIn and fAnOut instructions. Analog I/O requires an add-on SPice board such as SPice102011.
  • Approximately 5 times as much user flash memory - programs can now be up to approximately 25K.
  • The MS120 has Xwire to take advantage of a growing range of peripheral boards and networking with other SPLat controllers.
  • The MS120 has ModBus RTU slave capability


MS120: Connections and adjustments

The above picture shows all the connectors used for making external connections to the MS120, plus various adjustment points. These are:

Bidirectional digital I/O 0-3 and 4-7

There are two 6-pin connectors for 8 I/O points which can each be used as an input or an output. Each connector has 4 signal pins as well as connection to the positive supply voltage and to common (ground). The wiring for these is detailed separately for input mode and output mode.

Inputs 8 to 11

This is a single 6-pin connector for 4 input-only pins. These can be configured as a group to act as NPN or PNP inputs. The connector also has positive supply and common connections.

Power

This 5-pin connector is for applying power to the MS120. This is a special arrangement that allows for two separate power supplies so you can implement a variety of backup and/or battery standby schemes. The wiring and power supply schemes are described under MS120 Power supply.

Pwr link

The 2-position jumper helps determine the power supply scheme. This is detailed under MS120: Power supply

Comms

This is the connector you connect to for programming the MS120. This is NOT an RS232 connector. A PC232 programming cable adaptor is required for connecting to a PC for programming.

SPx

The SPx expansion scheme is documented elsewhere in the SPLat Knowledge Base. This connector is for expansion boards like the XBIO16.

SPice

This connector is for low cost SPice add-on boards. It is virtually identical to the SPice connector on MMi99, MMi200 and SL99 controllers. TSP is not supported. Normally you would simply plug a compatible board onto this connector. Some information about the connector is provided under MS120 SPice connectors.

SPice2

This is a second SPice connector, providing more SPice expansion pins in the MS120 than in previous models.

NPN/PNP link

This 2-position jumper determines whether inputs 8-11 operate in NPN or PNP mode. This is detailed under MS120: Dedicated NPN/PNP inputs.

Contrast

This is for adjusting the contrast on the optional liquid crystal display. It is factory preset and should normally not need adjusting.

Previous Topic

Next Topic

In This Chapter

Some common terminology

MS120: Where to get matching connectors


Some common terminology

If your background is not in the electronics field, we may confuse you a bit with the way we refer to power supply pins, grounds, "common" terminals etc. In electronics we generally quote all voltages in relation to an assumed common or reference terminal. That common terminal is often called "ground", "gnd" or "circuit ground". It may or may not actually be tied to a physical ground point, for instance mains power supply earth.

In SPLat boards we usually label the "ground" terminal "0V". On boards that are powered off an external DC power supply (which is most off-the-shelf boards), 0V is the terminal connected to the negative terminal of the power supply. Measured relative to that pin, therefore, the positive power supply pin will be 24V (or 12V or whatever the supply voltage is) positive. We then say "we have a +12V supply". All boards contain a 5V regulator chip to feed the microprocessor and/or other logic chips. Its output voltage is said to be +5V, again measured relative to the 0V terminal.

Similarly, all input and output voltages are also measured relative to the 0V terminal, and will in virtually all cases be positive voltages. For example, on most SPLat models the input voltage threshold for digital inputs, and analog input and output voltages, are measured relative to 0V.


MS120: Where to get matching connectors

All connections to the MS120 are via 0.1" (2.54mm) pitch single inline friction lock connectors. These are frequently referred to as "KK" type, which is the name given to them by Molex. All sale versions versions come with matching connectors, but you may want to source them in a different form for mass production manufacture of wiring looms.

You would normally buy the connectors as plastic housings plus (separately) crimp terminals. The Molex part numbers are 22-01-2xx5 for the housings (xx=number of ways) and 08-50-0032 for the terminals. You can buy these from Farnell, Mouser, Digikey and many other suppliers.

The connector sizes you need for the MS120 are:

Inputs and outputs: 3 x 6-way

Power: 1 x 5-way


MS120: Power supply

The power supply scheme on the MS120 is quite novel, and very versatile. It features:

  • 2 separate supply inputs with "OR" diodes.
  • The ability for your program to monitor the voltage on either supply input as an analog input.
  • A jumper (link) to select between two power-down modes.
  • Future feature: A low power mode for the controller (processor)

What this all means is that you can implement a range of backup, shutdown and standby schemes.

Basic operation

For normal, "everyday" operation without any fancy features, simply connect a 10-24VDC power supply to the terminals labeled "Main" and "+" and "0V" in the above picture, and place the jumper in position 'A'. Please see the article on common terminology for an explanation of the labeling.

Supply voltage

In general, the MS120 needs a supply voltage between 10VDC and 24VDC. The voltage is allowed to go to a maximum of 32V. Note that the MS120 operating temperature ratings depend on the supply voltage.

It's quite OK to use an unregulated power supply. Just remember that the ripple will make the voltage instantaneously less than the average DC value, and the board will respond to the instantaneous value. Remember, too, that an unregulated supply can go quite a bit higher than its nominal value, especially when lightly loaded and if the mains voltage is high.

If you are using any add-on boards with the MS120, for example a SPice board, you must take their power supply requirements into account. For example, the thermistor function on the SPice 10200 contains a programmable drive voltage that can go up to 20V. If you are using that function you must use a supply voltage at least 2V more than the thermistor drive voltage you are going to program.

We generally suggest customers buy an off the shelf 24V switchmode power supply. These are so inexpensive these days that it is uneconomic to do otherwise.

Supply current

The current drawn by the board will depend on the supply voltage, what the board is doing and on any add-ons. Allowing 15mA for a SPice board and 19mA for the LCD and backlight, maximum supply current draw versus supply voltage is:

Voltage | Max current | Temp
10V | 101mA | 70°C
12V | 104mA | 70°C
15V | 109mA | 70°C
18V | 113mA | 70°C
24V | 123mA | 60°C
28V | (129mA) | (47°C)
32V | (135mA) | (33°C)

The temperature column gives you the maximum allowable ambient temperature for operation of the power supply regulator. Note that other considerations, especially the digital output loading, may limit operation to lower temperatures. These figures are calculated based on everything being on at once. The figures do not include any external loads such as relays. You can deduct 19mA if the LCD is not fitted.

Note: Figures at 28V and 32V are given for reference only. We do not recommend operating the board at voltages over 24V for sustained periods of time. The board can be damaged if the power supply voltage goes over 32V, even momentarily.

Analog input monitoring of supply voltages

The two supply voltages can be measured by the controller as analog inputs. In both cases the input is scaled to 32.77V full scale (255 counts). That means that 24V will be 255*24/32.77=186.75 counts (which will come out as 186 or 187). With this you need to take into account an overall accuracy of about 5%, so in fact you could wind up with a reading of between 177 and 196.

The "Main" power input is read with an AnInA instruction, leaving the result in X. The "Bat" input is read with an AnInB instruction.

Advanced modes

The idea of having two power inputs, and monitoring them as analog inputs, is that you can implement various power management schemes.

With the jumper in the "A" position the two power input pins are interchangeable. However, if you set the jumper in the "B" position (illustrated above), they become different. In that mode, the actual processor chip will be powered from either supply pin but the "+" connections on the I/O connectors will only be powered from the "Main" power input.

What this means is that you can connect a battery to the "Bat" supply and power the board from a normal power supply. Providing the battery voltage is less than the power supply voltage, the power supply will provide power as long as it is operating. If the power goes off, however, the battery will take over and provide power to the processor but not to the I/O connector "+" pins. Assuming you have connected your output loads to the "+" pins of the I/O connectors, the loads will be powered down if mains (line) power fails, but the processor will still receive power. This will result in a reduced power consumption while running off battery. If your program is monitoring the power supply inputs it can then shut down the LCD backlight to reduce power consumption and then wait for the main power to be restored.

Other variations are possible. It is all a matter of what you may wish to achieve. Here are some ideas:

Rechargeable battery. A resistor and diode in series can trickle charge the battery off the main power supply. The diode prevents the battery from discharging back into the power supply.

Capacitor. Use a large value capacitor on the "Bat" terminal, charge it via a diode. A 1000µF capacitor will give about about 0.5s "holdup" if charged to 24V (assuming the jumper is in the "B" position so the capacitor is not powering external loads). That would be plenty of time to memorize the machine state in permanent memory.


MS120: External inputs and outputs

The MS120 contains 4 dedicated digital inputs and 8 points which can be used as inputs or outputs. We call the latter "bidirectional". In effect they consist of an input and an output brought out to a single pin. If the output is OFF, an externally connected device (say an NPN sensor or a contact) can control the on/off state of the pin. If the output is turned on, it connects the I/O pin to 0V via a transistor switch.

The input-only points have the additional feature that they can be used for NPN or PNP style sensors.

Previous Topic

Next Topic

In This Chapter

MS120: Bidirectional points used as inputs

MS120: Bidirectional points used as outputs

MS120: Dedicated NPN/PNP inputs


MS120: Bidirectional points used as inputs

The above picture shows the two bidirectional digital I/O connectors used with a couple of typical input devices. An input is deemed to be on when the I/O pin is connected to 0V through a contact or an NPN sensor.

Each connector contains the connections for 4 inputs, plus pins that connect onboard to the power supply positive and 0V terminals. The common pin on each input connector is provided simply as a convenience to you when wiring inputs to the board.

To program the point as an input simply never turn on the corresponding output (it defaults to off at power up). If your program does turn on the output, then as an input it will read ON.

In the picture A is a simple switch connected to I/O point 3. B is the output of an NPN style sensor connected to input 6. The sensor would also require a positive supply voltage to power it. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

As an input the bidirectional pins are ON when the pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - V(supply) / 10000

The leading minus sign simply means the current is flowing out of the pin. The 10000 represents the 10K input pullup resistance.

This equation translates to 1.2mA with a 12V supply and 2.4mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The bidirectional pins used as inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognise the input as ON when the voltage on the input pin is typically less than 4.6V (3V worst case). That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input is approximately 4.6V or a little less. However, due tolerances (variations) between batches of components an input is guaranteed to be treated as low (in our case ON) only if the voltage is below 3V. An input is guaranteed to be treated as high (OFF) only if the voltage is above 7V. The range between 3V and 7V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

Note that the above threshold voltages vary from product to product. Please check the specific documentation on the board(s) you are using.

How to blow up the bidirectional I/O points when used as inputs.

  1. Apply a voltage more negative than the 0V (common) terminal.
  2. Apply a voltage over +32V


MS120: Bidirectional points used as outputs

The adjacent picture illustrates the bidirectional I/O points connected to some typical output loads. When an output is ON it connects the I/O pin to 0V via a Darlington transistor.

Each of the two connectors is wired to 4 bidirectional I/O pins. Each connector also has a "convenience" pin connected to power supply positive. The output load should always be connected between the output pin and power supply positive. Use the +Supply convenience pin on the I/O connector in preference to wiring directly to the power supply +.

CAUTION: Take care never to short an output pin to the positive common pin. If the output is on at the time you will blow up the output chip instantly!

The loads illustrated are:

A: The input pins of a solid state relay. Note the polarity!

B: A relay coil. Notice we have included a catch diode across the coil. While there are diodes on the board we recommend a diode across the coil, as close as possible to the coil, to minimize problems with switching noise. A suitable diode is a 1N4001 or similar 1A/50V rectifier diode.

C: An LED, with a series current limiting resistor. Resistance calculators

The (digital) outputs are implemented using the ULN2803A, a chip that contains Darlington transistors. The outputs consist of transistor switches between the output pin and 0V. When the output transistor is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output chip will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built into the chip. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the relay coil as close as possible to the relay, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

Maximum allowable output current

The maximum allowable output current is 400mA steady state, with brief (100mS) peaks of 500mA allowed. There is also a limit to how much total current one ULN2803A chip can handle. This relates to the internal heating of the chip.

Let's look at 2 extreme cases:

  1. All 8 outputs on one chip conducting the same current
  2. The allowable current concentrated in a few outputs.

Equal currents

The following table lists allowable current versus ambient temperature, when all 8 outputs are conducting the same current at once:

Temperature | Allowable current per output
25°C | 180mA
40°C | 160mA
50°C | 145mA
60°C | 130mA
70°C | 115mA

Concentrated currents

The following table shows the maximum allowable currents, when concentrated in a few outputs, versus temperature

Temp | 1st O/P | 2nd O/P | 3rd O/P | 4th O/P
25°C | 400mA | 400mA | 300mA | 0
40°C | 400mA | 400mA | 150mA | 0
50°C | 400mA | 400mA | 0 | 0
60°C | 400mA | 325mA | 0 | 0
70°C | 400mA | 220mA | 0 | 0

Naturally there are many other possible combinations, but the above should serve as a guide.

The above figures are intended as a guide only. In any event, the absolute maximum operating temperature for the ULN2803A chip is 70°C.

If you feel like doing the actual sums, here's the procedure.

  1. For each output, calculate its output voltage drop from the formula
    V(out) = 0.92 + 1.85 * I(out) [ I(out) in Amperes ]
  2. For each output calculate the dissipation in watts from the formula
    P=V(out) * I(out)
  3. Add up all the dissipation figures to give P(tot) in watts
  4. Calculate the maximum allowable ambient temperature from the formula
    Ta(max) = 125 - P(tot) * 55
  5. If you get an answer over 70°C then use 70°C

This calculation aims at a maximum internal (junction) temperature of 125°C, which is the maximum rating of the chip. There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better.

Note: This discussion pertains to the maximum operating temperature for the ULN2803A output chip only. See MS120 Operating temperature ratings for a further discussion.

How to blow up the outputs

Here are some of the ways you can blow up the outputs:

  1. Connecting the output pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than the output current rating)
  2. Connecting the output pin to any negative voltage
  3. Connecting the output pin to any voltage greater than the positive supply.
  4. Switching a load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  5. Using an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.
  6. Driving an incandescent lamp. Incandescent lamps draw a warm-up surge current of many (up to 20) times their rated steady state current.

After you've blown up the outputs

We have thoughtfully fitted the ULN2803A output chip in a socket for you, so you can replace it without soldering. We can supply a 6-pack of ULN2803A chips. Our product code is ULNx6, and is available on our website. Alternatively you can buy the chip wherever fine chips are sold, including many electronics hobbyist stores.. Here are some supplier names and order codes:-

Supplier | Order code
Digikey (USA/International) | Search site
Mouser (USA/International) | 511-E-ULN2803A
Altronics (Australia) | Z3010
Element 14 (formerly Farnell Electronics) (UK, Europe, Australia, NZ) | 705-159, 409-868, 318-7032

The ULN2803A is made by many manufacturers, including Motorola, Texas Instruments, Alegro, Toshiba and ST Microelectronics. You should be able to buy them from most electronic component distributors and even from major electronic hobby stores.

When semiconductor switches fail they are more likely to become stuck ON than OFF.


MS120: Dedicated NPN/PNP inputs

The 4 dedicated input-only pins on the MS120 are able to be set to work as NPN or PNP inputs. There is 2-position configuration jumper (link) that sets the mode. All 4 inputs are set by the one jumper, so you cannot mix modes.

The picture above shows the jumper in the NPN position and a switch wired into the input in NPN mode. In NPN mode the switch must complete a connection between the input pin and circuit common (0V) for the input to be read as ON. The yellow arrow indicates the position of the selection jumper. The input current is

I(in) = - V(supply) / 10000

The leading minus sign simply means the current is flowing out of the pin. The 10000 represents the 10K input pullup resistance.

The picture above shows the jumper in the PNP position and a switch wired into the input in PNP mode. In PNP mode the switch must complete a connection between the input pin and the power supply positive terminal for the input to be read as ON. The input current is

I(in) = V(in) / 10000

V(in) is the externally applied voltage, which normally will be equal to the supply voltage. The 10000 represents the 10K input pullup resistance.

The final picture, above, shows a PNP type sensor connected to the MS120. The sensor is powered off the input connector. '+' on the sensor is it's positive power Please refer to the sensor manufacturer's technical data for the proper connections on the sensor.

Why have 2 different ways of doing it?

The reason we have 2 different ways are largely historical. NPN and PNP refer to the two basic types of bipolar transistor. Sensor manufacturers usually make sensors in both polarities, or sometimes they make sensors that can be used as either. It tends to cost less to make a NPN outputs (like our standard transistor outputs on SPLat) because modern bipolar transistor technology is biased in favor of NPN.

Which way is best?

Which of the two is best depends on your point of view. We have a preference for NPN because it makes designs a little easier and less expensive to make, so we can bring you more functionality per dollar. On the other hand, it is argued in some quarters that PNP is safer because an accidental short circuit to ground (the most probably kind of accidental short) is less likely to produce a spurious input. The validity of this argument will depend on the specific application.

We suggest that unless you have good reason otherwise, you use the NPN mode. This will improve your chances of making a control design that is capable of transferring to our other controllers (which at time of writing all use NPN).

Threshold voltages

The actual threshold voltage is nominally about 4.6V in either mode (min/max = 3/7) . In NPN mode anything less than the threshold is On. In PNP mode anything greater than the threshold is On. The processor knows the mode because the mode jumper is fed into an auxiliary processor input.

How to blow up the dedicated inputs

  1. Apply more than ±35V steady state for more than 10s to any one input.
  2. Apply more than ±100V momentarily


MS120: Operator interface

The MS120 has a built in operator interface (Man-Machine interface). The operator interface consists of:

5 push buttons

These are miniature momentary action push buttons, wired to dedicated inputs. They are sensed by a program just like any other input, though the SPLat programming language contains special instructions that are designed to simplify programming user interface operations. The buttons appear to your program on inputs 12 through 16.

4 LEDs

These are program-controlled front panel indicators. They are programmed like any other output. There is one pair of instructions for making it extremely easy to make them blink (flash). The LEDs appear to your program as outputs 8 through 11.

Liquid Crystal Display

The liquid crystal display, when fitted, is programmed according to the documentation for the "On-Board LCD". The LCD backlight is on/off controlled by output 20. It can also be dimmed in MS120 boards with the latest Firmware. Output 20 is shared as one of the pins of the second SPice connector (it also appears as input 25), but cannot be used as such on boards fitted with an LCD.

The backlight uses white LED technology, which is a technology with a lifetime of about 3000 hours. Your program should turn off or dim the backlight when not in use, especially in applications that are turned on 24/7. Example code for doing this is provided in Example LCD backlight auto-off timers.

Faceplate and graphic overlay (you supply)

The MS120 is designed to be mounted behind a flat faceplate. The plate has cutouts to match the buttons, LEDs, and if used, LCD. You will find dimensioned drawings in the files resources area of the resources CD or on our file resources FTP site. The board needs to be spaced off the faceplate so the tops of the push button switch stems fall minutely (<0.1mm) below the outside surface of the faceplate. You then place a flexible polyester or Lexan graphics overlay on the outside surface and the buttons can be activated by pressing them through the overlay. You can see more detail in the MMi202 documentation.

Warning: The board and the metal faceplate (or any other metallic surface that the board is mounted to) must be attached using metal screws and no insulating hardware so they are at the same potential. The mounting holes on the board are deliberately made with exposed metal. Failure to "bond" the face plate to the board will expose the board and the LCD to the risk of damage from static discharge.


MS120: Non-volatile memory

The term non-volatile memory means a form of memory that does not lose its data when the power goes off. It can be implemented using a variety of technologies. Your hard disk drive is non-volatile. The magnetic core memory used in computers in the 60's and 70's was non-volatile. Some systems use a small battery to keep the memory powered up even when main power is off. The technologies commonly used in SPLat boards are known as "EEPROM" and "FLASH" memory.

The controller implements three kinds of non-volatile memory: "Permanent" memory, "Shadow" memory and "Non-Volatile Extended Memory" (NVEM). The Permanent memory has existed in SPLat boards almost from the very beginning. Shadow memory is a much newer development made possible by advances in microcontroller technology. NVEM is even more recent, and was introduced into off the shelf products in dialect 16.

Permanent memory

This type of non-volatile memory was introduced into SPLat in March 1998. It allows just 13 bytes of RAM (data memory) to be saved into a non-volatile memory as a indivisible (all or nothing) block to be later restored. The two instructions for this are PermStore and PermRecall.

Shadow memory

This is newer type of non-volatile memory, introduced in March 2002 in language dialect 10. It allows all of RAM to be selectively backed up to a "shadow" memory in blocks of any size, and likewise to be restored in blocks of any size. It also allows the shadow memory to be cleared in blocks, and supports indexed and jndexed addressing for more sophisticated data management in larger projects.

The term shadow memory comes about because it is a region of non-volatile memory that overlaps the RAM. When a certain RAM location is written to the shadow memory, it is written to the same byte address within the shadow memory as its own RAM address.

The instructions for shadow memory are ShadowWrite, ShadowRead and ShadowClear. All these can be used as indexed as well as non-indexed instructions.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

NVEM page 0

The controller implements "NVEM page 0", which is a non-volatile memory that makes available any of the User Flash memory in the processor chip that is not used by your program.

Comparison between nonvolatile memory types

The following table compares the three nonvolatile memory types as implemented in most SPLat controllers. Actual figures vary between specific products. Make sure you read the descriptions of each type for your product.

Type | Size | Typical use | Endurance (in typical product) | Notes
Perm-mem | 13 bytes | Cycle counters, hour meters | 500K to 1M | Built in error detection. Can only be written and read as a single block. Does NOT survive downloading of a new program
Shadow | Same size as RAM | User configuration data | 10,000 total (not per byte!) | Very easy to use, works very well in menu systems. Can be read and written one byte at a time. Survives downloading of a new program
NVEM page 0 | However much of User Flash is not used by your program. Maximum 32K. | Lookup tables, infrequently changed configuration or pattern data, scripts | 10,000 per block. | Read often/write very infrequently. Writing is constrained by 512 (typical) byte write block size. Must be presumed corrupted if a new program is downloaded.

Endurance of non-volatile memories

Certain non-volatile memory technologies have a finite life, as defined by the number of writes or erases (clear cycles) they can endure. For permanent memory this translates to the total number of times a PermStore can be executed before the memory is likely to give erroneous data. For shadow memory it means the total number of ShadowWrite or ShadowClear instructions before it is likely to give errors. For NVEM0 it is the total of writes to any block of (typically) 512 bytes.

(In older products shadow memory was genuine byte-writable EEPROM, and the endurance was per byte. More recent models use main FLASH because the newer chips lack EEPROM, and each write entails re-writing the whole shadow block. Hence the endurance is now total writes to the block).

You should take great care to not write a program that will write or clear the non-volatile memory so often that it gets worn out too soon.

Example: Say a given non-volatile memory has a stated endurance of 100,000 cycles, and your program counts cans of sardines at a rate of 100 per minute, updating the non-volatile memory after every count. Your board would only last 1000 minutes, or about 2 shifts. If you re-write your program so it only updates the non-volatile memory once per hour, you would get 100,000 hours or about 12 years life, with some loss of accuracy as the trade-off.

CAUTION: You must take into account the possibility of power failing right in the middle of writing to the non-volatile memory. Unless specifically stated for the SPLat product you are using, there is always a possibility that data will be corrupted if that happen. You must therefore ensure, by the design of your system, that there will be no disastrous consequences of corrupted data.

The Permanent Memory has the best protection in this regard. Data is written with a checksum, and the PermRecall instruction provides a result indicator in the R register. This means that in the very least your program can have a good probability of avoiding the use of suspect data.

In the case of writing to NVEM page 0, the risk can be even worse. NVEM page zero follows directly on from the program memory. That means you could be writing to the same (typically 512 byte) block as the end of your program or read-only data tables. A power failure during a write would result in your program getting corrupted. You should therefore have an unused areas of 512 bytes between the program or read-only tables and the part of NVEM page 0 that is to be written to.

Caution: Exceeding the endurance of FLASH memory may damage the controller. It will also void the product warranty.

Support in SPLat/PC

Both types of non-volatile memory are supported in SPLat/PC.

The support for permanent memory is documented in the SPLat/PC "classic" help file. In the help file index search for "Permanent memory".

SPLat/PC simulates shadow memory by saving the shadow data in the computer's memory. The data is initialized to all zero's when SPLat/PC is first launched. After that it is affected only by simulated shadow memory instructions. That means you can stop and restart a simulation and the shadow memory data is retained. There is no simulation of an unsuccessful ShadowWrite or ShadowClear.

Previous Topic

Next Topic

In This Chapter

MS120: Permanent memory

MS120: Shadow memory

MS120: NVEM0


MS120: Permanent memory

Permanent memory in the MS120 is implemented in the main FLASH memory. By means of a proprietary circular re-use technique we achieve an endurance of 640,000 write cycles. The write and read functions are both very fast. The instructions that access permanent memory are PermStore and PermRecall.

Data in Permanent memory is reset to all zeros when a program is downloaded to the board.


MS120: Shadow memory

Shadow memory in the MS120 is implemented using main FLASH memory. Endurance is 10,000 writes or clears in total. ShadowWrite takes up to 20mS per byte. ShadowClear takes up to 10mS per byte. During these times the SPLat processor can do nothing else (for example quadrature counting). We use a smart algorithm to avoid redundant writes and clears, making the process as fast as possible and reducing wear on the memory.

ShadowRead is very fast.

Downloading a program to the controller does not change the contents of shadow memory.


MS120: NVEM0

NVEM0 (page 0 NVEM) in the MS120 is part of the same memory where your application program is stored. In fact, your program and NVEM0 share the same contiguous block of memory, called User Flash. The bigger your program, the less is available for NVEM0, and visa versa. In the MS120 there is approximately 21K of user flash. The exact amount is reported in the SPLat/PC Module window when you connect to a board. The amount will reduce in future Firmware versions as we use more of the total flash memory to implement new features. If you have developed a stable product and find a new firmware version has less memory you can always reFlash it back to the firmware you initially used to develop your application.

In the following description for the MS120, the block size is 512 bytes and the endurance is 10,000 writes (this varies from product to product).

The endurance figures given above are the chip maker's worst case ratings over all legal temperatures and supply voltages. They are very conservative but no guarantee is given that the chips can survive more writes. Exceeding the FLASH memory write endurance will be construed as mis-use and void our product warranty.

Boards with language dialect 15 or later implement NVEM0. This page provide details specific to the implementation in the SL99/100, MMi2xx, MS12x, CC16/18, RFD18, and HD8. General NVEM0 documentation is located elsewhere.

NVEM0 in these controllers is implemented using the processor's main FLASH memory. This memory can only be written in fixed size blocks. The block size differs between products.

What we have done, therefore is to create some "smoke and mirrors" to simulate byte-writable memory. That means your program can write to individual bytes, and they will be updated correctly. However, there are downsides:

  • Every time you write one byte all the bytes in the same block must also be written. That has a serious consequence for the endurance of the memory. Endurance refers to the unavoidable fact that FLASH memory wears out after a certain number of writes. What this means is that you must be very careful when using NVEM0 not to write to it too often.

    Our Firmware is smart enough to know that several bytes being written from one instruction only requires one single block write. Hence, writing a multi-byte record or a floating point number only counts as one "hit" on the endurance, but it does hit every byte in the block. But so does writing one single byte!
  • Because the processor must write a whole block at a time (actually, erase and then re-write the whole block), writes to NVEM0 are quite slow. Also, there is no way you can know where the block boundaries are relative to your program, so any write operation involving more than 1 byte could cross a boundary and therefore require 2 writes.

Whenever you change you program, and hence its size, the absolute addresses of all your NVEM0 data will change. That means a particular multibyte write operation could suddenly wind up straddling a block boundary and take twice as long. Be very cautious when evaluating the effect of write times on your program.

  • NVEM0 is located in the processor's Flash memory, directly after your program. There is a big danger in reading, erasing, modifying and re-writing the block that contains the end of your program and/or any block containing read-only tables. If the power should fail during the write you will not only lose your data, you will also damage your program (or tables) and most likely wreck your product. For this reason this NVEM0 implementation will not permit writing to the memory block that contains the end of your program. If that is attempted the board will suffer a fatal runtime error. You should therefore make sure that there are at least one block's worth bytes of not-written-to NVEM immediately before any NVEM0 that gets written to by your program. The best policy is to place all read-only tables directly after you program, then to pad out with one block of unused NVEM0.

Examples:

;Example 1: Padding with unused space, because I have no read-only tables:

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM page 0 memory

           NV0Space     XXXXX      ;Force new XXXXX byte block

NVData     ;Start defining any writable NVEM0 areas here

********* ********* ********* ********* ********* ********* ********* *********

;Example 2: Place read-only lookup tables first, then force a new byte block.

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM

;Table of sines 0-90', 10' increments (takes 40 bytes)

SineTable  NV0fNum      0          ; sin(0)

           NV0fNum      1.74E-1    ; sin(10)

           NV0fNum      3.42E-1    ; sin(20)

           NV0fNum      5E-1       ; sin(30)

           NV0fNum      6.43E-1    ; sin(40)

           NV0fNum      7.66E-1    ; sin(50)

           NV0fNum      8.66E-1    ; sin(60)

           NV0fNum      9.40E-1    ; sin(70)

           NV0fNum      9.85E-1    ; sin(80)

           NV0fNum      1          ; sin(90)

Padding    NV0Space     XXXXX      ;Force new block

;Any NVEM that gets written during program execution gets placed below this point.

;That would typically be large parameter tables that can be changed by an end-user.


MS120: Onboard counter type A (OBCA)

The controller incorporates an onboard counter type A (called OBCA). This is a high speed hardware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or liquid flow measurement using a turbine flow sensor.

The OBCA exists on several SPLat boards. Generic documentation for the feature is provided in the Onboard counter type A guide.

The OBCA is connected to input number 7. On the MS120 there is no input filtering to limit the maximum count frequency. The maximum count frequency, assuming a squarewave input, is approximately 100kHz. The accuracy is approximately 2% (this varies from product to product, depending on whether they use a crystal for timing or not. This board does not have a crystal).


MS120: OnBoard Counter type B

The board incorporates an onboard counter type B (called OBCB). This is a medium speed Firmware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or wind speed using a rotating vane anemometer.

The OBCB exists on several SPLat boards. Generic documentation for the feature is provided in the OnBoard Counter type B guide.

The OBCB is implemented on inputs 0 though 7. The maximum count frequency is approximately 1.8kHz, assuming a perfect square wave input. The accuracy is approximately 2% (this varies from product to product, depending on whether they use a crystal for timing or not. This board does not have a crystal).


MS120: OnBoard Quadrature Counter

The MS120 implements an OnBoard Quadrature Counter (OBQC) on inputs 0 and 1. A quadrature counter decodes the dual channel outputs of a quadrature encoder and maintains an up/down counter. A quadrature encoder is a (usually rotary) encoder that produces two output signal that indicate the amount of rotations and the direction of rotation.

You should make sure that the encoder cannot generate input state changes more often that 400uS apart. You should also make sure your program is designed to never perform any writes to non-volatile memory when it must track an encoder with the OBQC.

For more information see the generic OBQC documentation.


MS120: CPU device

The MS120 with late Firmware revisions can respond to UV instructions that address the CPU device. Some of the responses are dependent on the hardware configuration.

Board Type

The board will respond to the following board-type poll:

  SPxPoll2      3,!CPU

by placing its board type identification number on U0 and U1. The response is a function of the type (model) of board and the number of connected XPice expansion boards (e.g. XBIO16, XIRO16, HIO16). The numbers can be fetched from U0 and U1 by the PushU instruction.

The following table defines the responses for the MS120 and its predecessor MS12 with varying amounts of expansion:

Configuration | U0 | U1; MS120 | U1; MS12
Controller alone | 0 | 91 | 48
Controller+16XPice | 0 | 92 | 49
Controller+32XPice | 0 | 93 | 50
Controller+48XPice | 0 | 94 | 51
Controller+64XPice | 0 | 95 | 52

Note that you can discover in this way how much expansion has been added but not the type of expansion board.

Interpreting the number of XPice points can be a little tricky. For boards with segregated inputs and outputs, like XIRO16 and HIO16, each 8I/8O board corresponds to 16 XPice points. For the XBIO16, each bidirectional point corresponds to one input and one output (and appears as such on the SPLat/PC screen). That means one XBIO16 board gets counted as 32 XPice points although it only adds 16 I/O points to your configuration.

PWM base frequency (Dialect 20 or later)

The analog outputs on the board are generated by a pulse width modulated digital pulse train which is filtered (averaged) by an analog filter to produce a DC output. Normally the frequency of the PWM pulse train is 16kHz. The base frequency can however be altered using the instruction:

SPxCmd1          0,!CPU

with register U0 preset to a value between 0 and 7. The resulting PWM frequencies are given in the following table:

U0 | PWM frequency
0 | 16kHz (Default)
1 | 8kHz
2 | 4kHz
3 | 2kHz
4 | 1kHz
5 | 500Hz
6 | 250Hz
7 | 125Hz

Any other value will result in a fatal runtime error.

All analog output pins are affected. This feature is designed to be used with PWM output circuits such as DC motor speed controls. Be aware that a low PWM frequency will have an impact on any actual analog (filtered DC) outputs. Our filter circuits are normally designed for the default PWM frequency. Using a much lower frequency will produce ripple in the DC output.

When used with an AnOut instruction, the resolution is 8 bits. With fAnOut it is 10 bits.

Modify SuperTimer 10mS clock interval (Dialect 20 or later)

You can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.

LCD backlight dimming control

The following applies only to MS120 boards fitted with V3.20 or later firmware.

The following is old, and included for reference, or very special circumstances. The OBLCD_Dim instruction provides an easier way of controlling dimming.

The board/Firmware supports a timer that automatically dims the backlight after a set time interval with no user activity, as determined by presses of the front panel push buttons.

The code:

                SetU       0,BL_HighLevel
                SetU       1,BL_LowLevel
                SPxCmd2    1,!CPU

will set the maximum, undimmed (BL_HighLevel) and minimum, dimmed (BL_LowLevel) brightness levels of the backlight. The two levels must be in the range 0 (minimum) to 8 (maximum). Any other values will produce an error.

The code:

                SetU       0,BL_DimTime
                SPxCmd1    4,!CPU

Will set the auto-dim timer value to BL_DimTime minutes. The allowable range is 0 to 255 (4h15m). Using 0 dims the backlight instantly.

Example: LCD backlight auto-off timer.

In MS121 firmware 4.14 or later, the state of the backlight can be read back. The code

    SPxPoll1    8,!cpu        ;returns current back light level in U(0)

Use the PushU 0 instruction to get the number to X or use GoIfUT/GoifUF if you just want to know when the back light is on/off.

If you are using output 20 to control the back light, this CPU call will still work but return a value of either 8 for back light on or 0 for back light off.

CPU temperature readout (Dialect 21 through 27 (Deleted after 27))

An SPxPoll4 0,!CPU returns the floating point temperature of the CPU chip in °C at locations U(0) --> U(3)

An SPxPoll4 1,!CPU returns the floating point temperature of the CPU chip in °F at locations U(0) --> U(3)

Previous Topic

Next Topic

In This Section

CPU: Processor temperature


CPU: Processor temperature

In dialects 21 to 27 or later you can read out the temperature of the CPU (processor) chip itself. We deleted this in dialect 28 to recover memory space for more useful functions.

An SPxPoll4 0,!CPU returns the floating point temperature of the CPU chip in °C at locations U(0) --> U(3)

An SPxPoll4 1,!CPU returns the floating point temperature of the CPU chip in °F at locations U(0) --> U(3)

Once you have the temperature in U(0)-U(3) you can transfer it to W using UtoW 0


MS120: SPice connectors

This section describes the specifics of the SPice connectors on the MS120. The SPice expansion scheme in general is documented elsewhere in the SPLat Knowledge Base, under the SPLat Expansion Framework.

The MS120 has 2 SPice connectors. These are labeled SPice (the "main" one) and SPiceII (Roman numeral 2). The former is virtually identical electrically and mechanically to the single SPice connectors on MMi99, MMi200 and SL99 controllers. The latter is unique to the MS120. Note the MS120 does not support TSP.

The main SPice connector supports the same SPice boards that work on MMi and SL family products.

The SPiceII is different. It allows us to make SPice boards that are uniquely for the MS120 and straddle both connectors. You can still only use one SPice board.

The SPice connector signals are as follows:

Cnctr | Pin# | As input | As output | As AnIn | As AnOut | U(n) | Comments
SPice | 1 | Raw positive board supply voltage
SPice | 2 | +5V regulated. Permissible load 15mA
SPice | 3 | Not used (Other products have a -ve bias generator in this pin, but not the MS120)
SPice | 4 | 17 | 12* | C (2) |  | 0 |
SPice | 5 | 18 | 13* | D (3) |  | 1 |
SPice | 6 | 19 | 14* | E (4) |  | 2 |
SPice | 7 | 20 | 15* | F (5) |  | 3 |
SPice | 8 | 21 | 16* |  | J (9) | 4 |
SPice | 9 | 22 | 17 |  | I (8)* | 5 |
SPice | 10,11,12 | 0V (system ground)
SPice-II | 1 | Raw positive board supply voltage
SPice-II | 2 | +5V regulated. Permissible load 15mA
SPice-II | 3 | 23 | 18* | G (6) |  | 6 |
SPice-II | 4 | 24 | 19* | H (7) |  | 7 |
SPice-II | 5 | 25 | 20* |  |  | 8 | Also LCD B/light
SPice-II | 6 | 26 | 21* |  | K (10) | 9 |
SPice-II | 7 | 27 | 22* |  | L (11) | 10 |
SPice-II | 8 |  |  |  |  |  | Comms Rx (in)
SPice-II | 9 |  |  |  |  |  | Comms Tx (out)
SPice-II | 10 | 0V (system ground)

* Default settings are marked *

The U(n) column refers to the position in U of the pin's configuration byte during a SPiceConfigU instruction.

Analog channel designators are show both in the old letter style and the newer (dialect 18 floating point analog I/O) numeric channel numbers.

The I/O pins can be set up for different functions for use with different SPice boards. The function of the pins is set in your program using the SPiceConfigU instruction.

One pin is shared with the LCD backlight control. If an LCD is fitted that pin is not available for any other use.

Note that the LCD backlight, output 20, defaults to OFF. Your program must turn it on if you are using the optional onboard LCD. See also Example LCD backlight auto-off timers.

The analog output function is actually a pulse width modulated (PWM) digital output. On a SPice board this can be filtered to yield a smooth DC voltage, giving an analog output.

The 15mA permissible load on the +5V supply pin is a nominal allowance, aggregate for the two connectors, that has been included in the overall power supply budget with board supply voltages up to the maximum rating of 32V. This can be increased significantly if lower supply voltages are used.

The raw board supply voltage referred to is as selected by the power jumper.

Comms Rx and Tx are the same signals that appear on the comms connector. They are CMOS/TTL voltage levels, not RS232. Being available on the SPice-II means we can make SPice boards with communications capabilities.

We do not intend users to use the SPice connector other than for add-on boards supplied by us. The above information is provided mainly to make you aware of the possibilities, perhaps to inspire you to have a custom SPice board made.


MS120: Comms connector

The MS120 does not have an RS232 communications interface. The COMMS connector works at 5V CMOS/TTL levels. For programming you need to use a special programming cable part number PC232. This is the same cable used with the CC18 and numerous custom controller.


MS120: Communications

The MS120 implements 3 serial communications protocols: Our proprietary SPLatLink, the industry standard ModBus RTU (master or slave), and the "User programmable protocol".

These protocols use the programming (serial) port. This is a "TTL compatible" serial port. It requires a TTL to RS232 converter to convert it to industry standard RS232. Our PC232 programming cable is suitable for this. With the programming cable it will connect directly to any PC that has an RS232 (COM port), or to any other device that has an RS232 "DTE" port. The maximum cable length is 5m. You can also use a TL485 adaptor to convert to RS485, which can go hundreds of meters.

User programmable protocol

This refers to the ability of SPLat to be programmed to send and received arbitrary characters. More information.

ModBus RTU Slave

ModBus is the oldest and most widely accepted protocol in automation. This controller supports ModBus RTU slave mode. In the initial implementations the ModBus feature allows message lengths up to the ModBus limit of 255 bytes. This may be reduced in the future as we add new features and have to "take back" some of the memory. In practice a 64-byte message can transfer 57 bytes of data.

More information on ModBus.

ModBus RTU Master

This controller supports ModBus RTU Master mode.

SPLatLink

SPLatLink is our proprietary protocol for communicating with a SPLat controller while it is running your SPLat Program.

SPLatLink is deprecated. We discourage its use in custom applications.


MS120: Xwire

The MS120 with Firmware dialect 20 and later supports Xwire. Xwire is a method of connecting several boards together into a multiprocessor network.

The Xwire connections are on connector X1. The board's Xwire address is set via your program, if it is to be a slave.

Xwire documentation.

The maximum number of data bytes that can be transferred into or out of the MS120 via Xwire is 32.

Electrically the MS120 can drive 10 unit loads and represents one unit load. That means one MS120 board can drive 10 other MS120s or other "unit load" boards.

The master configuration table may contain a maximum of 50 entries. This is more than the hardware limit. Conceivably one slave board might have more than one logical device (address) on it.


MS120: Programming

The MS120 is a member of the SPLat family of controllers. That means that it shares its programming language and programming tools with all other SPLat controllers. Whatever you learn on the MS120 is applicable to other models. The only differences will be those relating to particular hardware resources and features, and dialect differences. "Dialect" is a term we use to describe differences between versions of the SPLat language, which really means that we are continually adding new language features which are not available on older controller products.

You program the board by writing and debugging a program to suit your application in your PC. You then download the program to the board and complete debugging and overall system testing. Once the program has been downloaded to a SPLat board it will remain secure in the board. You can download a new program to the board at any time, in which case the old one is overwritten. You cannot upload a program from a board back into your computer, so it is impossible for a competitor to steal your program out of a board.

The PC software you use for programing is called SPLat/PC. SPLat/PC gives you a wide range of programming and debugging tools. It incorporates a help file with reference material and our classic, highly acclaimed, programming tutorial. You can download SPLat/PC free from here.

SPLat/PC also features a series of semi-interactive "mini-tutorials". These are designed to give you a bit of a kick start with SPLat. You should start your exploration there.

The proprietary SPLat programming language contains a repertoire of over 400 instructions. These instructions cover a huge range of functions. The instruction set differs from conventional programming languages and microprocessor assembler languages in that it has a strong emphasis on the needs of control applications. In particular, there is a subset of just 14 instructions called FastTrack. The FastTrack subset contains all you may ever need in order to implement simple timing and sequencing applications with batch counting and multiple interlocks.

In December 2005 we introduced MultiTrack programming. This is an extremely easy method of writing programs that can (seemingly) do several things at once. (For experts: MultiTrack is a non-preemptive multitasking scheduler that is extremely easy to learn and use). MultiTrack extends FastTrack, so that with just 16 instructions you can write useful multitasking programs. The mini-tutorials in SPLat/PC include an introduction to MultiTrack. It has been our experience over many years that a lot of people shy away from MultiTrack because multitasking is a large conceptual leap. The MultiTrack mini-tutorial is excellent in that regard, as it presents the concept in an extremely approachable way. Even if it takes you several hours to wrap your head around, invest the time! The investment will pay off handsomely in anything but the most trivial program. After you have grasped the basics of MultiTrack, your next port of call should be the more detailed MultiTrack tutorial.

The 400+ instructions that comprise the full SPLat programming language are documented within the SKB in the Programming Reference. Some of these are instructions we have introduced to simplify common control tasks even further, such as blinking an LED, and form an extension of the FastTrack concept. Therefore, it's a good idea to look there once you move on from pure FastTrack.

If you need even more knowledge you can work with the tutorial built into the SPLat/PC help file. It starts with a FastTrack tutorial that approaches FastTrack that is different from the approach taken in the online course (this is older material), then goes into the "classic" tutorial. The classic tutorial starts off with the nitty gritty details of the internal registers in SPLat. It then works up from there with a detailed expose, along with many examples, of the vast majority of SPLat instructions and how they are used.

Note: The MS120 requires a special adaptor cable for programming. This is supplied with the MS120DK216 developer's kit.

You can also program this controller in C. Click here for details.

Previous Topic

Next Topic

In This Chapter

MS120: Programming cable


MS120: Programming cable

The controller needs to be connected to a PC for program download. The full process of connecting to the PC is described in the help file that comes with SPLat/PC, our programming software.

For this controller you need a special SPLat programming cable, PC232. This cable is supplied with the Developer's Kit, and may also be ordered separately through our website. The cable plugs into a 9-pin serial (COM) port on a PC.

You also need a computer with a serial (COM) port. Modern notebook computers tend not to have COM ports. In that case you will need a USB to Serial adaptor. Not all such adaptors work equally well. The ones we find work are based on the FTDI chipset. These are available on our website.


MS120: MultiTrack

Controllers with dialect 16 or greater contain the MultiTrack multitasking system. The capacity is 32 concurrent tasks.


MS120: Operating temperature ratings

The maximum allowable operating temperature of the board is governed by several factors.To determine the highest permissible operating temperature you need to evaluate each factor separately and then use the worst (lowest temperature) answer.

The factors that must be taken into account are:

  1. The LCD, if you are using one, has a maximum operating temperature of 50°C and storage temperature of 60°C. Please contact us for extended temperature range displays (special order).
  2. The semiconductors on the board have a maximum ambient temperature rating of 70°C.
  3. The 5V power supply regulator will shut down and self-protect if its junction temperature exceeds 150°C. How hot it actually gets depends heavily on the supply voltage.

    The 5V regulator can operate at 70°C ambient for all conditions of loading, including 3mA for the optional LCD and 15mA for the SPice connectors, at supply voltages up to 18V. Above 18V you need to derate the upper temperature limit as shown under power supply details.

  4. The chip driving the outputs will also self-heat, and eventually fail if run too hot for too long. Evaluating this factor is discussed under MS120 External digital outputs
  5. Hot components fail faster than cool components. For semiconductors, lowering the temperature by 10°C doubles the life time.

The minimum operating temperature is -40°C with the proviso that there be no water condensation. Conformally coated boards can be supplied to special order to minimize the effects of condensation.


MS120: Firmware revisions and reFlashing

This chapter contains the two related topics of Firmware revisions/updates and reFlashing.

Previous Topic

Next Topic

In This Chapter

MS120: reFlashing

MS120: Firmware version history and updates

MS12: Firmware version history and updates


MS120: reFlashing

reFlash is the ability of a SPLat board to have new operating Firmware loaded into it. The operating firmware is the program that we load into the board, that gives it its SPLat "personality". This is quite different from the program you write to tailor it to your application, which we call SPLatWare.

reFlash is available in 2nd generation boards, starting with the MMi99 with firmware version 3.3 or later (reFlash was developed after the earlier MMi99 versions were released). Older designs, which do not have reFlash, are SP108, SL88, MMi88, AJ18, PMC3, CA20, OEM32 and OEM36.

There are three reasons why you would ever want to reFlash a SPLat board:

  1. Because we introduce new firmware features quite frequently, it has become impossible to ensure that all our (and our dealers') inventory is fully up to date. Therefore it is possible that a board you receive does not contain the "latest and greatest". Thanks to the miracle of reFlash technology you can still update your board to the latest version.
  2. We goofed, and there's a bug in the existing firmware. For example a SPLat instruction giving incorrect results.
  3. We devised a new feature (for example new instructions) and you'd like to upgrade your existing board(s). This makes most sense if you keep a board for development. There is little point in upgrading boards already shipped in your product, unless you are also upgrading the SPLatWare and you have used new language features.

reFlashing is discussed more thoroughly elsewhere in the SKB.


MS120: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for MS120. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date

rfl file

Description

V3.30

8Nov11

ms120v3-30.rfl

  • Bug fixed: Sometimes fails to connect to SPLat/PC when expansion boards present.

V3.28

9Jul11

ms120v3-28.rfl

  • Dialect 28 support

V3.27

20May11

ms120v3-27.rfl

  • Dialect 27 support (enhanced user protocol processing)

V3.26

23Aug10

ms120v3-26.rfl

  • Dialect 23 support
  • Various enhancements to Xwire
  • Various bugs fixed

V3.22

20Jan10

ms120v3-22.rfl

  • Dialect 21 support
  • Fast program download support. Works with SPLat/PC 10.21.1 and later
  • OBLCD instructions can be mapped to an external HMI via ModBus
  • CPU temperature readout

V3.20

25Mar09

ms120v3-20.rfl

  • Added ModBus Master RTU mode
  • Atomic handling of multi-byte variables in Xwire receive
  • LCD backlight is now dimmable with an automatic timer
  • Extended LCD custom characters to allow large format 7-segment digit generation
  • Improved accuracy of analog outputs

V3.17

17Jun08

ms120v3-17.rfl

  • Bug fix - Xwire

V3.16

10Jun08

ms120v3-16.rfl

  • Bug fix - SPxMode values other than 7 giving input data errors in SPxTxfrU

V3.15

13May08

ms120v3-15.rfl

  • Bug fix - Xwire
  • Bug fix - MemtoUV and UVtoMem not passing the last byte
  • Bug fix - FixToU clobbering W
  • Bug fix - Spurious front panel push button activations generated when updating LCD.

V3.13

29Aug07

ms120v3-13.rfl

  • Bug fix - under unlikely circumstances Xwire could get scrambled

V3.12

16Aug07

ms120v3-12.rfl

  • Dialect 20, adds Xwire, 10mS timebase adjustment
  • Discontinues TSP

V3.11

16Jul07

ms120v3-11.rfl

  • Dialect 19, adds OBCB
  • Fixed bug in STScale
  • Improved interrupt handling for expansion I/O to reduce processor loading

V3.1

15Jan07

ms120v3-1.rfl

Fixes bug in PermStore

V3.0

N/A

Initial release, dialect 18


MS12: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for MS12. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date

rfl file

Description

V3.16

18Sep06

MS12v3-16.rfl

Fixed bug introduced in V3.15

V3.15

5Sep06

MS12v3-15.rfl

Dialect 18 upgrade: fAnIn, fAnOut, ClrInstCount, OBLCD_HGraph

V3.13

10Jan06

MS12v3-13.rfl

Dialect 16 upgrade, adding over 70 new instructions and a raft of new features, including:

  • MultiTrack
  • SuperTimers (10mS to 46 hours)
  • Quadrature counting
  • NVEM page 0
  • Enhanced LCD buffering

V3.11

12Nov04

MS12v3-11

Fixed bug in fSetTimer that gave wrong result for W=0

V3.1

N/A

Initial release.


MS120 specific accessories

Previous Topic

Next Topic

In This Chapter

MS120TB terminal board (for production use)


MS120TB terminal board (for production use)

The MS120TB (shown mounted on an MS120B board) converts the plug-in I/O and power connections to screw terminals for easy wiring in low-volume production situations.

This unit will only work on the MS120B board, which has I/O pins at right angles to the main board. Earlier models, MS12 and MS120 (non-B) had their I/O pins parallel to the board.

Using the MS120TB

When you receive the MS120TB, the mounting hardware will be loose. You need to assemble it yourself. Use the picture above as a guide. You get the correct board to board spacing with one tubular spacer and a flat nylon washer (highlighted in the picture). The terminal board is permanently fixed to the controller.When plugging the two boards together, take care to align the connectors properly. It is perfectly normal for a small part of the pins on the controller to be visible when the two boards are connected.

External input and output devices are connected using the same principles as connecting directly to the controller board. The only difference is that the plug-in pins of the MS120 have been extended to screw terminals.


CC18 product documentation

Entry level pricing, up scale performance!

Congratulations on your choice of the SPLat CC18 controller. SPLat gives you the ability to implement a sophisticated control system in your product easily and inexpensively, and will give you an edge that will have your competitors wondering whatever hit them!

The SPLat CC18 controller gives you a feature set rivalling competing products costing many times as much. Although costing about the same as "programmable relays", entry level single board PLCs and hobbyist products, it offers considerably more capabilities.

  • Easier to program than C, Basic or ladder;
  • Large program memory (over 20K bytes, approx 12,000 instructions);
  • Capable of sophisticated control functions including math, state machines, PID, algorithms, heuristics and lookup tables;
  • Built in multitasking;
  • Real-world I/O: 400mA outputs and ruggedized inputs;
  • 2 analog inputs (not in CC16)
  • Bicolor programmable status LED (not in CC16)
  • Able to be networked with other Xwire compatible SPLat controller and peripherals
  • Fast: Executes approximately 15,000 high level SPLat instructions per second. (2.5x faster than previous models);
  • Expandable.

Have your first working program in less than an hour!

Like all SPLat controllers, the CC18 is extremely easy to get started with. Our programming software is free, and includes a full simulator that allows you to test your program and explore possibilities without any hardware present. Our programming language has been skillfully crafted to make the simple tasks absurdly easy to program, and yet there is a rich repertoire of advanced instructions to satisfy the sophisticated user. Our training materials are legendary (and free). The CC18 contains real world input/output circuits, so you won't have to design your own.

Every minute you invest in SPLat is an investment that lasts!

Best of all, the CC18 programming is fully compatible with our other controllers. That means if you move up to say an MMi202, or even go for a custom SPLat controller one day, everything you have learnt and 98% of the code you have written will transfer directly. SPLat is the only control system you can get started with for the price of a night out and then stay with until you are using thousands of custom units.

How do we do it?

You may be sceptical about how we can deliver so much performance at such a budget price. When we design a controller we aim for the best possible value for money. We design and re-design our circuitry to eliminate unnecessary or expensive components. As far as possible we select multi-vendor components so we can shop around. Maybe most importantly, we are engineers designing for end users, not engineers designing for engineers. We don't allow ourselves to be carried away by megahertz and gigabytes. We understand that what is really important to an OEM operating in the real world is getting the necessary performance at a price that will give a competitive advantage.

One thing we are obsessive about is build quality. We hate returns, and get very few of them (way less than 1%). Every board we make is built with strict process controls. Every board is visually inspected. Every board is 100% functionally tested. And every board is backed by our 2-year standard product guarantee and Post-Nuke repair policy.

Previous Topic

Next Topic

In This Chapter

CC18: Introduction

CC18: Connections

CC18: Power supply

CC18: External inputs and outputs

CC18: Non-volatile memory

CC18: OnBoard Counter type A (OBCA)

CC18: OnBoard Counter type B

CC18: OnBoard Quadrature Counter (OBQC)

CC18: Bicolor status LED

CC18: CPU device

CC18: Comms connector

CC18: Communications

CC18: Xwire

CC18: Programming

CC18: MultiTrack

CC18: Timing accuracy

CC18: Operating temperature ratings

CC18: Firmware revisions and reFlashing

CC16/18 specific accessories


CC18: Introduction

This document provides you with detailed technical information on the SPLat CC18 programmable controller.

This document is part of the SPLat Knowledge Base (SKB). The SKB is a comprehensive collection of documents covering all technical aspects of the SPLat product. It is published in electronic formats (HTML-Help and HTML) and is also available electronically as a series of document files you can print out yourself. By publishing the documents only in electronic form, we not only save some trees but are also able to make updates available to you very quickly on our website. We suggest you check our Website periodically to ensure you have the most recent version.

Previous Topic

Next Topic

In This Chapter

Getting started with your controller

CC18: Using this document

Other resources

CC18: Product overview

Some common terminology


Getting started with your controller

You are possibly about to take your first step with a SPLat controller. We at SPLat Controls hope you find it a rewarding and fun process. We have worked very hard to make SPLat as easy to use, as versatile and as capable as possible. As part of that effort we have created a number of resources for you: tools, educational materials, documentation and more.

You will get the most out of all these materials if you give yourself time to at least get an overview of what is available.

The key resources are:

Mini Tutorials

The SPLat/PC programming software contains a number of "MiniTutes". These are very brief interactive tutorials that give you a quick overview of some aspect of SPLat. Each one takes 5-10 minutes. Even if you have never written a program before, the very first MiniTute, "Quick tour", will have you saying "Ahah!" within 15 minutes. The only skill you need to get started is how to use a computer. If you have got this far you probably have that.

Tutorials in the SKB

The SPLat Knowledge Base contains tutorials on a number of topics.

SPLat/PC programming software

This is the program you use to program your SPLat controller to do what you want it to do. This is a comprehensive program that provides program entry, simulation, downloading to the controller and more.

SPLat/PC is on the SPLat Resources CD, and you can also get the very latest version off our website at any time.

'Classic' help file

The help file that's part of SPLat/PC was the original electronic documentation for SPLat. It remains the only documentation for first generation SPLat controllers, and is still the principal source of reference material and knowledge on the basic internal workings of SPLat (registers etc.)

The classic help file installs with SPLat/PC. You can download the latest SPLat/PC here.

With the advent of Vista and Windows 7, Microsoft has dropped built-in support for its own classic help file format *.hlp. To be able to use the SPLat Classic help file on those systems you will need to download and install WinHlp32.exe. Click here for more information at Microsoft.


CC18: Using this document

This document concerns itself primarily with information that is specific to the CC18 product. In particular, it concentrates on the hardware aspects of the CC18. Because programming is uniform across all SPLat products, detailed SPLat programming information is located elsewhere. For more introductory information on programming please see CC18 Programming within this document.

The material is organized largely according to various hardware features. This should allow you to quickly locate the information you need in order to properly match the CC18 to the outside world and make electrical connections. There are also a couple of chapters on more general topics like determining the permissible operating temperature.

We suggest you start with a quick read through the whole document to familiarize yourself with what's there. After that you should be able to use it as a technical reference to look up specific information you need.

Most technical product manuals contain a Specifications section. We've done it a bit differently (we do many things differently, which is probably why you are using SPLat!). The numbers and ratings are discussed as part of each feature. While this may seem to make the information a little harder to locate (compared to a consolidated table), it gives us the ability to loosen up a little and discuss how the operating envelope can be flexed under different conditions. For example, the allowable output current depends on the operating ambient temperature.


CC18: Product overview

The CC18 is a SPLat single board programmable logic controller designed to provide excellent value in application requiring basic digital logic I/O. It features:

  • 16 digital I/O points that can each be used an an input or an output.
  • Up to 500mA capability on each output.
  • Industry standard NPN inputs.
  • 2 analog inputs
  • Programmable bicolor status LED
  • Xwire network port
  • Wide power supply voltage range
  • Supports standard SPLat XPice expansion board like XBIO16, XIRO16, HIO16, FP16 and MBIO16.

While the CC18 is very affordable it is nevertheless a complete control solution with real-world inputs and outputs.

The CC18 is supplied in different packages.

  • CC18DK. This is the Developer's Kit. It consists of the CC18 board plus programming software, programming cable and a set of matching connectors;
  • CC18OEM. This is the "production" version of the board (only) with matching connectors.


CC18: Connections

The above picture shows all the connectors used for making external connections to the CC18. These are:

Bidirectional digital I/O 0-3, 4-7, 8-11 and 12-15

There are four 6-pin connectors for 16 I/O points which can each be used as an input or an output. Each connector has 4 signal pins as well as connection to the positive supply voltage and to 0V (common, ground). The wiring for these is detailed separately elsewhere for input mode and output mode.

Analog

This 4-pin connector is for connecting to the two analog inputs. In addition to the two actual analog input pins the connector has +5V and 0V brought out to facilitate using potentiometers or ratiometric sensors.

Power

This 3-pin connector is for applying power to the CC18. The wiring is described under CC18: Power supply

Comms.

This is the connector you connect to for programming the CC18. This is NOT an RS232 connector. A PC232 programming cable adaptor is required for connecting to a PC for programming.

SPx

The SPx expansion scheme is documented elsewhere in the SPLat Knowledge Base. This connector is for expansion boards like the XBIO16.

X1-X2

Xwire bus connections. The two connectors are identical and wired in parallel. The lefthand pin as shown is 0V (ground). The righthand pin is bidirectional serial data.

A1/A0

Configuration jumpers setting the Xwire address. If you are not using Xwire you can read these using the XwireGetJumpers instruction and use them in your program for selecting user options.

Previous Topic

Next Topic

In This Chapter

CC18: Where to get matching connectors


CC18: Where to get matching connectors

All connections to the CC18 are via 0.1" (2.54mm) pitch single inline friction lock connectors. These are frequently referred to as "KK" type, which is the name given to them by Molex. All sale versions versions come with matching connectors, but you may want to source them in a different form for mass production manufacture of wiring looms.

You would normally buy the connectors as plastic housings plus (separately) crimp terminals. The Molex part numbers are 22-01-2xx5 for the housings (xx=number of ways) and 08-50-0032 for the terminals. You can buy these from Farnell, Mouser, Digikey and many other suppliers.

The connector sizes you need for the CC18 are:

Inputs and outputs: 4 x 6-way

Power: 1 x 3-way


CC18: Power supply

The above illustration shows the power connections to the CC18. We have used a 3-pin connector, to differentiate from 2-pin connectors used elsewhere on the board. There are two pins for 0V (gnd, common). You do not have to wire both to both 0V pins; either one will do. The wire shown in red above should go to the positive terminal of your power supply. The black wire should go to the ground or common terminal of the power supply (often labelled minus on a single voltage supply). See also 'Some common terminology':

Supply voltage

In general, the CC18 needs a supply voltage between 10VDC and 24VDC. The voltage is allowed to go to a maximum of 32V. Note that the CC18 operating temperature ratings depend on the supply voltage.

It's quite OK to use an unregulated power supply. Just remember that the ripple will make the voltage instantaneously less than the average DC value, and the board will respond to the instantaneous value. Remember, too, that an unregulated supply can go quite a bit higher than its nominal value, especially when lightly loaded and if the mains voltage is high. In general we suggest you use a 24VDC switchmode power supply. These are now so inexpensive that there is little reason not to use one.

Supply current

The current drawn by the board will depend on the supply voltage and what the board is doing. The maximum supply current draw versus supply voltage is:

Voltage | Max current | Temp
10V | 67mA | 70°C
12V | 71mA | 70°C
15V | 77mA | 70°C
18V | 83mA | 70°C
24V | 95mA | 70°C
28V | (103mA) | (70°C)
32V | (111mA) | (70°C)

The temperature column gives you the maximum allowable ambient temperature for operation of the power supply regulator. Note that other considerations, especially the digital output loading, may limit operation to lower temperatures. These figures are calculated based on everything being on at once. The figures do not include any external loads such as relays.

Note: Figures at 28V and 32V are given for reference only. We do not recommend operating the board at voltages over 24V for sustained periods of time. The board can be damaged if the power supply voltage goes over 32V, even momentarily.


CC18: External inputs and outputs

The CC18 contains 16 I/O points which can each be used as inputs or outputs. We call these "bidirectional". In effect they consist of an input and an output brought out to a single pin. If the output is OFF, an externally connected device (say an NPN sensor or a contact) can control the on/off state of the pin. If the output is turned on, it connects the I/O pin to 0V via a transistor switch.

Previous Topic

Next Topic

In This Chapter

CC18: Bidirectional points used as inputs

CC18: Bidirectional points used as outputs

CC18: Analog inputs


CC18: Bidirectional points used as inputs

The above picture shows two bidirectional digital I/O points used with a couple of typical input devices. An input is deemed to be on when the I/O pin is connected to 0V through a contact or an NPN sensor.

Each connector contains the connections for 4 inputs, plus pins that connect onboard to the power supply positive and 0V terminals. The common pin on each input connector is provided simply as a convenience to you when wiring inputs to the board.

To program the point as an input simply never turn on the corresponding output (it defaults to off at power up). If your program does turn on the output, then as an input it will read ON.

In the picture A is a simple switch connected to I/O point 13. B is the output of an NPN style sensor connected to input 1. The sensor would also require a positive supply voltage to power it. That is shown as a red wire from the V+ convenience output on the CC18. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

As an input the bidirectional pins are ON when the pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - V(supply) / 10000

The leading minus sign simply means the current is flowing out of the pin. The 10000 represents the 10K input pullup resistance.

This equation translates to 1.2mA with a 12V supply and 2.4mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognise the input as ON when the voltage on the input pin is typically less than 4.6V (3V worst case). That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input is approximately 4.6V or a little less. However, due tolerances (variations) between batches of components an input is guaranteed to be treated as low (in our case ON) only if the voltage is below 3V. An input is guaranteed to be treated as high (OFF) only if the voltage is above 7V. The range between 3V and 7V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

Note that the above threshold voltages vary from product to product as well as from unit to unit. Please check the specific documentation on the board(s) you are using.

How to blow up the bidirectional I/O points when used as inputs.

  1. Apply a voltage more negative than the 0V (common) terminal.
  2. Apply a voltage over +32V


CC18: Bidirectional points used as outputs

The picture above illustrates the bidirectional I/O points connected to some typical output loads. When an output is ON it connects the I/O pin to 0V via a Darlington transistor.

Each of the fours connectors is wired to 4 bidirectional I/O pins. Each connector also has a "convenience" pin connected to power supply positive. The output load should always be connected between the output pin and power supply positive. Use the Supply+ convenience pin in preference to wiring directly to the power supply +.

CAUTION: Take care never to short an output pin to the positive common pin. If the output is on at the time you will blow up the output chip instantly!

The loads illustrated are:

A: The input pins of a solid state relay. Note the polarity!

B: A relay coil. Notice we have included a catch diode across the coil. While there are diodes on the board we strongly recommend a diode across the coil, as close as possible to the coil, to minimize problems with switching noise. A suitable diode is a 1N4001 or similar 1A/50V rectifier diode.

C: An LED, with a series current limiting resistor. Resistance calculators

You will (of course!) have noticed that for A and C we returned the loads to V+ by way of the convenience outlet pins on the boards, whereas for B we have shown the connection to be to V+ elsewhere (presumable directly to the power supply). There are two reasons for this:

  1. In general, you can do whatever is most convenient.
  2. Notwithstanding 1 above, there is some advantage in returning higher current loads directly to the power supply, especially inductive loads like relay coils and solenoids. By not having the supply currents of these loads flow through the controller there is less chance of the noise "spikes" they generate interfering with the controller.

The (digital) outputs are implemented using the ULN2803A, a chip that contains Darlington transistors. The outputs consist of transistor switches between the output pin and 0V. When the output transistor is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output chip will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built into the chip. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the relay coil as close as possible to the relay, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

Maximum allowable output current

The maximum allowable output current is 400mA steady state, with brief (100mS) peaks of 500mA allowed. There is also a limit to how much total current one ULN2803A chip can handle. This relates to the internal heating of the chip.

Let's look at 2 extreme cases:

  1. All 8 outputs on one chip conducting the same current
  2. The allowable current concentrated in a few outputs.

Equal currents

The following table lists allowable current versus ambient temperature, when all 8 outputs are conducting the same current at once:

Temperature | Allowable current per output
25°C | 180mA
40°C | 160mA
50°C | 145mA
60°C | 130mA
70°C | 115mA

Concentrated currents

The following table shows the maximum allowable currents, when concentrated in a few outputs, versus temperature

Temp | 1st O/P | 2nd O/P | 3rd O/P | 4th O/P
25°C | 400mA | 400mA | 300mA | 0
40°C | 400mA | 400mA | 150mA | 0
50°C | 400mA | 400mA | 0 | 0
60°C | 400mA | 325mA | 0 | 0
70°C | 400mA | 220mA | 0 | 0

Naturally there are many other possible combinations, but the above should serve as a guide.

The above figures are intended as a guide only. In any event, the absolute maximum operating temperature for the ULN2803A chip is 70°C.

If you feel like doing the actual sums, here's the procedure.

  1. For each output, calculate its output voltage drop from the formula
    V(out) = 0.92 + 1.85 * I(out) [ I(out) in Amperes ]
  2. For each output calculate the dissipation in watts from the formula
    P=V(out) * I(out)
  3. Add up all the dissipation figures to give P(tot) in watts
  4. Calculate the maximum allowable ambient temperature from the formula
    Ta(max) = 125 - P(tot) * 55
  5. If you get an answer over 70°C then use 70°C

This calculation aims at a maximum internal (junction) temperature of 125°C, which is the maximum rating of the chip. There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better.

Note: This discussion pertains to the maximum operating temperature for the ULN2803A output chip only. See CC18 Operating temperature ratings for a further discussion.

How to blow up the outputs

Here are some of the ways you can blow up the outputs:

  1. Connecting the output pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than the output current rating)
  2. Connecting the output pin to any negative voltage
  3. Connecting the output pin to any voltage greater than the positive supply.
  4. Switching a load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  5. Using an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.

Driving an incandescent lamp. Incandescent lamps draw a warm-up surge current of many (up to 20) times their rated steady state current.


CC18: Analog inputs

This does not apply to the CC16

The CC18 has two analog voltage inputs. The input voltage range is 0 to Vcc, where Vcc is the +5V nominal onboard logic power supply. The onboard 5V regulator (which is effectively the analog reference voltage) is accurate to ±5%. The resolution is 10 bits (0.1%).

These inputs are very well suited to use with the ratiometric sensor devices that are becoming common. These sensors run off a 5V logic supply and give an output proportional to the supply voltage X the quantity being measured. The inputs will also work very well with potentiometers.

The following diagram shows the analog pinning and a potentiometer connected to one input.

This circuit will give a floating point analog reading (fAnIn 0 instruction) of 0 with the wiper at the top of the diagram (at the 0V end of the potentiometer) and 1.0 at the other end.

How to blow up the analog inputs

  • Apply more than ±24V to the analog input pins.
  • Apply any external voltage to the +5V pin.


CC18: Non-volatile memory

The term non-volatile memory means a form of memory that does not lose its data when the power goes off. It can be implemented using a variety of technologies. Your hard disk drive is non-volatile. The magnetic core memory used in computers in the 60's and 70's was non-volatile. Some systems use a small battery to keep the memory powered up even when main power is off. The technologies commonly used in SPLat boards are known as "EEPROM" and "FLASH" memory.

The controller implements three kinds of non-volatile memory: "Permanent" memory, "Shadow" memory and "Non-Volatile Extended Memory" (NVEM). The Permanent memory has existed in SPLat boards almost from the very beginning. Shadow memory is a much newer development made possible by advances in microcontroller technology. NVEM is even more recent, and was introduced into off the shelf products in dialect 16.

Permanent memory

This type of non-volatile memory was introduced into SPLat in March 1998. It allows just 13 bytes of RAM (data memory) to be saved into a non-volatile memory as a indivisible (all or nothing) block to be later restored. The two instructions for this are PermStore and PermRecall.

Shadow memory

This is newer type of non-volatile memory, introduced in March 2002 in language dialect 10. It allows all of RAM to be selectively backed up to a "shadow" memory in blocks of any size, and likewise to be restored in blocks of any size. It also allows the shadow memory to be cleared in blocks, and supports indexed and jndexed addressing for more sophisticated data management in larger projects.

The term shadow memory comes about because it is a region of non-volatile memory that overlaps the RAM. When a certain RAM location is written to the shadow memory, it is written to the same byte address within the shadow memory as its own RAM address.

The instructions for shadow memory are ShadowWrite, ShadowRead and ShadowClear. All these can be used as indexed as well as non-indexed instructions.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

NVEM page 0

The controller implements "NVEM page 0", which is a non-volatile memory that makes available any of the User Flash memory in the processor chip that is not used by your program.

Comparison between nonvolatile memory types

The following table compares the three nonvolatile memory types as implemented in most SPLat controllers. Actual figures vary between specific products. Make sure you read the descriptions of each type for your product.

Type | Size | Typical use | Endurance (in typical product) | Notes
Perm-mem | 13 bytes | Cycle counters, hour meters | 500K to 1M | Built in error detection. Can only be written and read as a single block. Does NOT survive downloading of a new program
Shadow | Same size as RAM | User configuration data | 10,000 total (not per byte!) | Very easy to use, works very well in menu systems. Can be read and written one byte at a time. Survives downloading of a new program
NVEM page 0 | However much of User Flash is not used by your program. Maximum 32K. | Lookup tables, infrequently changed configuration or pattern data, scripts | 10,000 per block. | Read often/write very infrequently. Writing is constrained by 512 (typical) byte write block size. Must be presumed corrupted if a new program is downloaded.

Endurance of non-volatile memories

Certain non-volatile memory technologies have a finite life, as defined by the number of writes or erases (clear cycles) they can endure. For permanent memory this translates to the total number of times a PermStore can be executed before the memory is likely to give erroneous data. For shadow memory it means the total number of ShadowWrite or ShadowClear instructions before it is likely to give errors. For NVEM0 it is the total of writes to any block of (typically) 512 bytes.

(In older products shadow memory was genuine byte-writable EEPROM, and the endurance was per byte. More recent models use main FLASH because the newer chips lack EEPROM, and each write entails re-writing the whole shadow block. Hence the endurance is now total writes to the block).

You should take great care to not write a program that will write or clear the non-volatile memory so often that it gets worn out too soon.

Example: Say a given non-volatile memory has a stated endurance of 100,000 cycles, and your program counts cans of sardines at a rate of 100 per minute, updating the non-volatile memory after every count. Your board would only last 1000 minutes, or about 2 shifts. If you re-write your program so it only updates the non-volatile memory once per hour, you would get 100,000 hours or about 12 years life, with some loss of accuracy as the trade-off.

CAUTION: You must take into account the possibility of power failing right in the middle of writing to the non-volatile memory. Unless specifically stated for the SPLat product you are using, there is always a possibility that data will be corrupted if that happen. You must therefore ensure, by the design of your system, that there will be no disastrous consequences of corrupted data.

The Permanent Memory has the best protection in this regard. Data is written with a checksum, and the PermRecall instruction provides a result indicator in the R register. This means that in the very least your program can have a good probability of avoiding the use of suspect data.

In the case of writing to NVEM page 0, the risk can be even worse. NVEM page zero follows directly on from the program memory. That means you could be writing to the same (typically 512 byte) block as the end of your program or read-only data tables. A power failure during a write would result in your program getting corrupted. You should therefore have an unused areas of 512 bytes between the program or read-only tables and the part of NVEM page 0 that is to be written to.

Caution: Exceeding the endurance of FLASH memory may damage the controller. It will also void the product warranty.

Previous Topic

Next Topic

In This Chapter

CC18: Permanent memory

CC18: Shadow memory

CC18: NVEM0


CC18: Permanent memory

Permanent memory in the CC18 is implemented in the main FLASH memory. By means of a proprietary circular re-use technique we achieve an endurance of 640,000 write cycles. The write and read functions are both very fast. The instructions that access permanent memory are PermStore and PermRecall.

Data in Permanent memory is reset to all zeros when a program is downloaded to the board.


CC18: Shadow memory

Shadow memory in the CC18 is implemented using a fixed block of flash memory. Endurance is at least 10,000 ShadowWrite or ShadowClear instructions total (worst case over temperature, typical is 100,000). This is the aggregate for the whole memory, not per byte. Each instruction takes about 20mS. During these times the SPLat processor can do nothing else (for example quadrature counting). We use a smart algorithm to avoid redundant writes and clears, making the process as fast as possible and reducing wear on the memory.

ShadowRead is very fast.

Downloading a program to the controller does not change the contents of shadow memory.


CC18: NVEM0

NVEM0 (page 0 NVEM) in the CC18 is part of the same memory where your application program is stored. In fact, your program and NVEM0 share the same contiguous block of memory, called User Flash. The bigger your program, the less is available for NVEM0, and visa versa. In the CC18 there is approximately 21K of user flash. The exact amount is reported in the SPLat/PC Module window when you connect to a board. The amount will reduce in future Firmware versions as we use more of the total flash memory to implement new features. If you have developed a stable product and find a new firmware version has less memory you can always reFlash it back to the firmware you initially used to develop your application.

In the following description for the CC18, the block size is 512 bytes and the endurance is 10,000 writes minimum (this varies from product to product).

The endurance figures given above are the chip maker's worst case ratings over all legal temperatures and supply voltages. They are very conservative but no guarantee is given that the chips can survive more writes. Exceeding the FLASH memory write endurance will be construed as mis-use and void our product warranty.

Boards with language dialect 15 or later implement NVEM0. This page provide details specific to the implementation in the SL99/100, MMi2xx, MS12x, CC16/18, RFD18, and HD8. General NVEM0 documentation is located elsewhere.

NVEM0 in these controllers is implemented using the processor's main FLASH memory. This memory can only be written in fixed size blocks. The block size differs between products.

What we have done, therefore is to create some "smoke and mirrors" to simulate byte-writable memory. That means your program can write to individual bytes, and they will be updated correctly. However, there are downsides:

  • Every time you write one byte all the bytes in the same block must also be written. That has a serious consequence for the endurance of the memory. Endurance refers to the unavoidable fact that FLASH memory wears out after a certain number of writes. What this means is that you must be very careful when using NVEM0 not to write to it too often.

    Our Firmware is smart enough to know that several bytes being written from one instruction only requires one single block write. Hence, writing a multi-byte record or a floating point number only counts as one "hit" on the endurance, but it does hit every byte in the block. But so does writing one single byte!
  • Because the processor must write a whole block at a time (actually, erase and then re-write the whole block), writes to NVEM0 are quite slow. Also, there is no way you can know where the block boundaries are relative to your program, so any write operation involving more than 1 byte could cross a boundary and therefore require 2 writes.

Whenever you change you program, and hence its size, the absolute addresses of all your NVEM0 data will change. That means a particular multibyte write operation could suddenly wind up straddling a block boundary and take twice as long. Be very cautious when evaluating the effect of write times on your program.

  • NVEM0 is located in the processor's Flash memory, directly after your program. There is a big danger in reading, erasing, modifying and re-writing the block that contains the end of your program and/or any block containing read-only tables. If the power should fail during the write you will not only lose your data, you will also damage your program (or tables) and most likely wreck your product. For this reason this NVEM0 implementation will not permit writing to the memory block that contains the end of your program. If that is attempted the board will suffer a fatal runtime error. You should therefore make sure that there are at least one block's worth bytes of not-written-to NVEM immediately before any NVEM0 that gets written to by your program. The best policy is to place all read-only tables directly after you program, then to pad out with one block of unused NVEM0.

Examples:

;Example 1: Padding with unused space, because I have no read-only tables:

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM page 0 memory

           NV0Space     XXXXX      ;Force new XXXXX byte block

NVData     ;Start defining any writable NVEM0 areas here

********* ********* ********* ********* ********* ********* ********* *********

;Example 2: Place read-only lookup tables first, then force a new byte block.

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM

;Table of sines 0-90', 10' increments (takes 40 bytes)

SineTable  NV0fNum      0          ; sin(0)

           NV0fNum      1.74E-1    ; sin(10)

           NV0fNum      3.42E-1    ; sin(20)

           NV0fNum      5E-1       ; sin(30)

           NV0fNum      6.43E-1    ; sin(40)

           NV0fNum      7.66E-1    ; sin(50)

           NV0fNum      8.66E-1    ; sin(60)

           NV0fNum      9.40E-1    ; sin(70)

           NV0fNum      9.85E-1    ; sin(80)

           NV0fNum      1          ; sin(90)

Padding    NV0Space     XXXXX      ;Force new block

;Any NVEM that gets written during program execution gets placed below this point.

;That would typically be large parameter tables that can be changed by an end-user.


CC18: OnBoard Counter type A (OBCA)

The controller incorporates an onboard counter type A (called OBCA). This is a high speed hardware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or liquid flow measurement using a turbine flow sensor.

The OBCA exists on several SPLat boards. Generic documentation for the feature is provided in the Onboard counter type A guide.

The OBCA is connected to input number 7. On the CC18 there is no input filtering to limit the maximum count frequency. The maximum count frequency, assuming a squarewave input, is approximately 100kHz. The accuracy is approximately 2% (this varies from product to product, depending on whether they use a crystal for timing or not. This board does not have a crystal).


CC18: OnBoard Counter type B

The board incorporates an onboard counter type B (called OBCB). This is a medium speed Firmware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or wind speed using a rotating vane anemometer.

The OBCB exists on several SPLat boards. Generic documentation for the feature is provided in the OnBoard Counter type B guide.

The OBCB is implemented on inputs 0 though 7. The maximum count frequency is approximately 1.8kHz, assuming a perfect square wave input. The accuracy is approximately 2% (this varies from product to product, depending on whether they use a crystal for timing or not. This board does not have a crystal).


CC18: OnBoard Quadrature Counter (OBQC)

The CC18 implements an OnBoard Quadrature Counter (OBQC) on inputs 0 and 1. A quadrature counter decodes the dual channel outputs of a quadrature encoder and maintains an up/down counter. A quadrature encoder is a (usually rotary) encoder that produces two output signal that indicate the amount of rotations and the direction of rotation.

You should make sure that the encoder cannot generate input state changes more often that 400uS apart. You should also make sure your program is designed to never perform any writes to non-volatile memory when it must track an encoder with the OBQC.

For more information see the generic OBQC documentation.


CC18: Bicolor status LED

This does not apply to the CC16

The "power" LED on the CC18 is actually a red/green bicolor LED which you can program for 8 different color and blinking. This can make it a useful status display. The programming is described under CC18: CPU device.


CC18: CPU device

The CC18 with late Firmware revisions can respond to UV instructions that address the CPU device. Some of the responses are dependent on the hardware configuration.

Board Type

The board will respond to the following board-type poll:

  SPxPoll2      3,!CPU

by placing its board type identification number on U0 and U1. The response is a function of the type (model) of board and the number of connected expansion boards (e.g. XBIO16, XIRO16, HIO16). The numbers can be fetched from U0 and U1 by the PushU instruction.

The following table defines the responses for the CC18 with varying amounts of expansion:

Configuration | U0 | U1
CC18 | 0 | 118
CC18+16Exp | 0 | 119
CC18+32Exp | 0 | 120
CC18+48Exp | 0 | 121
CC18+64Exp | 0 | 122

Note that you can discover in this way how much expansion has been added but not the type of board.

Interpreting the number of XPice points can be a little tricky. For boards with segregated inputs and outputs, like XIRO16 and HIO16, each 8I/8O board corresponds to 16 XPice points. For the XBIO16, each bidirectional point corresponds to one input and one output (and appears as such on the SPLat/PC screen). That means one XBIO16 board gets counted as 32 XPice points although it only adds 16 I/O points to your configuration.

PWM base frequency (Dialect 20 or later)

The analog outputs on the board are generated by a pulse width modulated digital pulse train which is filtered (averaged) by an analog filter to produce a DC output. Normally the frequency of the PWM pulse train is 16kHz. The base frequency can however be altered using the instruction:

SPxCmd1          0,!CPU

with register U0 preset to a value between 0 and 7. The resulting PWM frequencies are given in the following table:

U0 | PWM frequency
0 | 16kHz (Default)
1 | 8kHz
2 | 4kHz
3 | 2kHz
4 | 1kHz
5 | 500Hz
6 | 250Hz
7 | 125Hz

Any other value will result in a fatal runtime error.

All analog output pins are affected. This feature is designed to be used with PWM output circuits such as DC motor speed controls. Be aware that a low PWM frequency will have an impact on any actual analog (filtered DC) outputs. Our filter circuits are normally designed for the default PWM frequency. Using a much lower frequency will produce ripple in the DC output.

When used with an AnOut instruction, the resolution is 8 bits. With fAnOut it is 10 bits.

Modify SuperTimer 10mS clock interval (Dialect 20 or later)

You can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.

CC18 status LED

The status LED on the CC18 (not the CC16) is programmed with the following sequence

        SetU			0,LEDMode
        SPxCmd1		2,!CPU

The value of LEDMode determines what you get:

LEDMode | Result
0 | Off
1 | Green
2 | Red
3 | Orange
4 | Blink red/green
5 | Blinking green
6 | Blinking red
7 | Blinking orange

CPU temperature readout (Dialect 21 through 27 or later (Deleted after 27))

An SPxPoll4 0,!CPU returns the floating point temperature of the CPU chip in °C at locations U(0) --> U(3)

An SPxPoll4 1,!CPU returns the floating point temperature of the CPU chip in °F at locations U(0) --> U(3)


CC18: Comms connector

The CC18 does not have an RS232 communications interface. The COMMS connector works at 5V CMOS/TTL levels. For programming you need to use a special programming cable part number PC232. This is the same cable used with the CC16, MS12, MS120 and numerous custom controller.


CC18: Communications

The CC18 implements 3 serial communications protocols: Our proprietary SPLatLink, the industry standard ModBus RTU (master or slave), and the "User programmable protocol".

These protocols use the programming (serial) port. This is a "TTL compatible" serial port. It requires a TTL to RS232 converter to convert it to industry standard RS232. Our PC232 programming cable is suitable for this. With the programming cable it will connect directly to any PC that has an RS232 (COM port), or to any other device that has an RS232 "DTE" port. The maximum cable length is 5m. You can also use a TL485 adaptor to convert to RS485, which can go hundreds of meters.

User programmable protocol

This refers to the ability of SPLat to be programmed to send and received arbitrary characters. More information.

ModBus RTU Slave

ModBus is the oldest and most widely accepted protocol in automation. This controller supports ModBus RTU slave mode. In the initial implementations the ModBus feature allows message lengths up to the ModBus limit of 255 bytes. This may be reduced in the future as we add new features and have to "take back" some of the memory. In practice a 64-byte message can transfer 57 bytes of data.

More information on ModBus.

ModBus RTU Master

This controller supports ModBus RTU Master mode.

SPLatLink

SPLatLink is our proprietary protocol for communicating with a SPLat controller while it is running your SPLat Program.

SPLatLink is deprecated. We discourage its use in custom applications.


CC18: Xwire

The CC18 (and CC16 with Firmware dialect 20 and later) supports Xwire. Xwire is a method of connecting several boards together into a multiprocessor network.

The Xwire connections are on connectors X1 and X2. Jumpers A0 and A1 can be used for setting the board's Xwire address via your program, if it is to be a slave. See XwireSetAddr

Xwire documentation.

The maximum number of data bytes that can be transferred into or out of the CC18 via Xwire is 32.

Electrically the CC18 can drive 10 unit loads and represents one unit load. That means one CC18 board can drive 10 other CC18s or other "unit load" boards.

The master configuration table may contain a maximum of 50 entries. This is more than the hardware limit. Conceivably one slave board might have more than one logical device (address) on it.


CC18: Programming

The CC18 is a member of the SPLat family of controllers. That means that it shares its programming language and programming tools with all other SPLat controllers. Whatever you learn on the CC18 is applicable to other models. The only differences will be those relating to particular hardware resources and features, and dialect differences. "Dialect" is a term we use to describe differences between versions of the SPLat language, which really means that we are continually adding new language features which are not available on older controller products.

You program the board by writing and debugging a program to suit your application in your PC. You then download the program to the board and complete debugging and overall system testing. Once the program has been downloaded to a SPLat board it will remain secure in the board. You can download a new program to the board at any time, in which case the old one is overwritten. You cannot upload a program from a board back into your computer, so it is impossible for a competitor to steal your program out of a board.

The PC software you use for programing is called SPLat/PC. SPLat/PC gives you a wide range of programming and debugging tools. It incorporates a help file with reference material and our classic, highly acclaimed, programming tutorial. You can download SPLat/PC free from here.

SPLat/PC also features a series of semi-interactive "mini-tutorials". These are designed to give you a bit of a kick start with SPLat. You should start your exploration there.

The proprietary SPLat programming language contains a repertoire of over 400 instructions. These instructions cover a huge range of functions. The instruction set differs from conventional programming languages and microprocessor assembler languages in that it has a strong emphasis on the needs of control applications. In particular, there is a subset of just 14 instructions called FastTrack. The FastTrack subset contains all you may ever need in order to implement simple timing and sequencing applications with batch counting and multiple interlocks.

In December 2005 we introduced MultiTrack programming. This is an extremely easy method of writing programs that can (seemingly) do several things at once. (For experts: MultiTrack is a non-preemptive multitasking scheduler that is extremely easy to learn and use). MultiTrack extends FastTrack, so that with just 16 instructions you can write useful multitasking programs. The mini-tutorials in SPLat/PC include an introduction to MultiTrack. It has been our experience over many years that a lot of people shy away from MultiTrack because multitasking is a large conceptual leap. The MultiTrack mini-tutorial is excellent in that regard, as it presents the concept in an extremely approachable way. Even if it takes you several hours to wrap your head around, invest the time! The investment will pay off handsomely in anything but the most trivial program. After you have grasped the basics of MultiTrack, your next port of call should be the more detailed MultiTrack tutorial.

The 400+ instructions that comprise the full SPLat programming language are documented within the SKB in the Programming Reference. Some of these are instructions we have introduced to simplify common control tasks even further, such as blinking an LED, and form an extension of the FastTrack concept. Therefore, it's a good idea to look there once you move on from pure FastTrack.

If you need even more knowledge you can work with the tutorial built into the SPLat/PC help file. It starts with a FastTrack tutorial that approaches FastTrack that is different from the approach taken in the online course (this is older material), then goes into the "classic" tutorial. The classic tutorial starts off with the nitty gritty details of the internal registers in SPLat. It then works up from there with a detailed expose, along with many examples, of the vast majority of SPLat instructions and how they are used.

Previous Topic

Next Topic

In This Chapter

CC18: Programming cable


CC18: Programming cable

The controller needs to be connected to a PC for program download. The full process of connecting to the PC is described in the help file that comes with SPLat/PC, our programming software.

For this controller you need a special SPLat programming cable, PC232. This cable is supplied with the Developer's Kit, and may also be ordered separately through our website. The cable plugs into a 9-pin serial (COM) port on a PC.

You also need a computer with a serial (COM) port. Modern notebook computers tend not to have COM ports. In that case you will need a USB to Serial adaptor. Not all such adaptors work equally well. The ones we find work are based on the FTDI chipset. These are available on our website.


CC18: MultiTrack

The CC18 fully supports our MultiTrack multitasking system, with a capacity of 32 concurrent tasks.


CC18: Timing accuracy

The CC18 uses an "on-chip RC oscillator" to generate all timing. The oscillator frequency is trimmed during factory testing at our plant. The accuracy is about 2%. This means that all time intervals and generated and measured will be subject to that much error. If you application requires better accuracy please use one of our other models with a crystal oscillator.


CC18: Operating temperature ratings

The maximum allowable operating temperature of the board is governed by several factors.To determine the highest permissible operating temperature you need to evaluate each factor separately and then use the worst (lowest temperature) answer.

The factors that must be taken into account are:

  1. The semiconductors on the board have a maximum ambient temperature rating of 70°C.
  2. The chip driving the outputs will also self-heat, and eventually fail if run too hot for too long. Evaluating this factor is discussed under CC18 External digital outputs
  3. Hot components fail faster than cool components. For semiconductors, lowering the temperature by 10°C doubles the life time.

The minimum operating temperature is -40°C with the proviso that there be no water condensation. Conformally coated boards can be supplied to special order to minimize the effects of condensation.


CC18: Firmware revisions and reFlashing

This chapter contains the two related topics of Firmware revisions/updates and reFlashing.

Previous Topic

Next Topic

In This Chapter

CC18: reFlashing

CC18: Firmware version history and updates

CC16: Firmware version history and updates


CC18: reFlashing

reFlash is the ability of a SPLat board to have new operating Firmware loaded into it. The operating firmware is the program that we load into the board, that gives it its SPLat "personality". This is quite different from the program you write to tailor it to your application, which we call SPLatWare.

reFlash is available in 2nd generation boards, starting with the MMi99 with firmware version 3.3 or later (reFlash was developed after the earlier MMi99 versions were released). Older designs, which do not have reFlash, are SP108, SL88, MMi88, AJ18, PMC3, CA20, OEM32 and OEM36.

There are three reasons why you would ever want to reFlash a SPLat board:

  1. Because we introduce new firmware features quite frequently, it has become impossible to ensure that all our (and our dealers') inventory is fully up to date. Therefore it is possible that a board you receive does not contain the "latest and greatest". Thanks to the miracle of reFlash technology you can still update your board to the latest version.
  2. We goofed, and there's a bug in the existing firmware. For example a SPLat instruction giving incorrect results.

We devised a new feature (for example new instructions) and you'd like to upgrade your existing board(s). This makes most sense if you keep a board for development. There is little point in upgrading boards already shipped in your product, unless you are also upgrading the SPLatWare and you have used new language features.

reFlashing is discussed more thoroughly elsewhere in the SKB.


CC18: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for CC18. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date

rfl file

Description

V3.23

4Nov11

cc18v3-23.rfl

  • Dialect 28 support

V3.19

20Aug10

cc18v3-19.rfl

  • Dialect 23 support
  • Various enhancements to Xwire
  • Various bugs fixed

V3.15

20Jan10

cc18v3-15.rfl

  • Dialect 21 support
  • Fast program download support. Works with SPLat/PC 10.21.1 and later
  • OBLCD instructions can be mapped to an external HMI via ModBus
  • CPU temperature readout
  • ModBus master mode added
  • Atomic handling of multi-byte variables in Xwire receive

V3.12

17Jun08

cc18v3-12.rfl

  • Bug fixes in Xwire


CC16: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for CC16. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date

rfl file

Description

V3.14

17Jun08

cc16v3-14.rfl

  • Various features and bug fixes

V3.13

29Aug07

cc16v3-13.rfl

  • Bug fix - under unlikely circumstances Xwire could get scrambled

V3.12

16Aug07

cc16v3-12.rfl

  • Dialect 20, adds Xwire, 10mS timebase adjustment
  • Discontinues TSP

V3.11

16Jul07

cc16v3-11.rfl

  • Dialect 19, adds OBCB
  • Fixed bug in STScale
  • Improved interrupt handling for expansion I/O to reduce processor loading

V3.1

cc16v3-1.rfl

Fixes bug in PermStore

V3.00

Nov06

N/A

Initial release, dialect 18


CC16/18 specific accessories

See also TL485 RS485 adaptor

Previous Topic

Next Topic

In This Chapter

CCBB breakout board (for development use)

CC16TB terminal board (for production use)


CCBB breakout board (for development use)

Note: Early revisions of this board are labelled CC16BB. Later revisions are CCBB.

The CCBB is a plug-on board designed to make your life easier during development of a CC16/18 controller application. It features:

  • Screw terminals for all 16 digital I/O points, with several convenience connections to V+ and 0V;
  • Screw terminals for feeding power, with reverse polarity protection;
  • LED indicators on all I/O points;
  • DIP switches to allow you to force inputs ON;
  • Convenient plug-on/plug off to the CC16/18 board;
  • Supplied with mounting hardware.

Using the CCBB

When you receive the CCBB, the mounting hardware will be loose. You need to assemble it yourself. Use the picture above as a guide. The breakout board is not permanently fixed to the controller. The screws simply act as locating pins and feet. The CCBB is intended only as a temporary attachment while you develop your program. When plugging the two boards together, take care to align the connectors properly. It is perfectly normal for a small part of the pins on the controller to be visible when the two boards are connected.

The above picture shows the CCBB from above. You can connect power for the whole assembly through the pair of screw terminals on the left. That connection has a polarity protection diode.

You can also use the regular power connector on the CC16/18 itself, if you prefer. It is not advisable to feed power in to the V+ terminals on the long edges of the breakout board, as these are not polarity protected. They are intended as a convenience for supplying power to external devices.

While you are experimenting with a program you can use the DIP switches and indicator LEDs to stand in for real external I/O devices. Just remember that each I/O point is actually an output wired in parallel with an input. To use it as an input the corresponding output must be OFF. If the output is turned ON, the LED will turn on and the corresponding input will register as ON. In all cases ON means there is a connection from the pin to 0V (ground, circuit common).

External input and output devices are connected using the same principles as connecting directly to the controller board. The only difference is that the plug-in pins of the CC16/18 have been extended to screw terminals.

When you do connect external devices, make sure that the DIP switches are turned OFF.

The best way to become comfortable with the breakout board is to hook everything together, connect to SPLat/PC and experiment with the I/O window in SPLat/PC.

The CCBB does not support the CC18 analog inputs.


CC16TB terminal board (for production use)

The CC16TB gives you a convenient way of connecting to the I/O and power pins of the CC16/18 controller using screw terminal blocks.It features:

  • Screw terminals for all 16 I/O points, with several convenience connections to V+ and 0V;
  • Screw terminals for feeding power, with reverse polarity protection;
  • Supplied with mounting hardware.

Using the CC16TB

When you receive the CC16TB, the mounting hardware will be loose. You need to assemble it yourself. Use the picture above as a guide. You get the correct board to board spacing with one tubular spacer and a flat nylon washer. The terminal board is permanently fixed to the controller. The hexagonal posts also give you the ability to mount the whole assembly to a base plate. The screws double as locating pins for the board to board connection. When plugging the two boards together, take care to align the connectors properly. It is perfectly normal for a small part of the pins on the controller to be visible when the two boards are connected.

You can connect power for the whole assembly through the pair of screw terminals marked 0V/V+ IN (the separate pair closest to the camera in the picture). That connection has a polarity protection diode. The other is for convenience and comes after the protection diode, so anything connected to them will also be protected against reverse polarity.

You can also use the regular power connector on the CC16/18 itself, if you prefer.

External input and output devices are connected using the same principles as connecting directly to the controller board. The only difference is that the plug-in pins of the CC16/18 have been extended to screw terminals.

The CC16TB does not support the CC18 analog inputs.


MMi203 product documentation

Congratulations on your choice of the SPLat MMi203. The MMi203 is the latest product in a family line that started in 1998 with the MMi88. The MMi88 was the world's first user-programmable controller with a built in operator interface and user-customizable graphics overlay. Since then the product family has evolved with an LCD and many more onboard features, and is our most successful standard product concept.

The MMi203 will allow you to implement a control system in your product with a raft of useful and interesting features like user menus, Real Time Clock and communications. You will find the programming is extremely easy, thanks to our FastTrack and MultiTrack programming. We help you get started with a huge collection of instructional materials. If you should get stuck, our support is second to none - just send a tech support request through our website contacts page.

This document covers both the MMi203 and its predecessors the MMi202, MMi201, MMi200 and MMi99.

The MMi200 was an upgraded MMi99, using a larger capacity processor chip on the same printed circuit board.

The MMi203 is a further enhanced design with identical physical dimensions. The main difference is twice as much program memory.

Previous Topic

Next Topic

In This Chapter

MMixxx: Introduction

MMi203: Making external connections

MMi203: Power supply

MMi203: External inputs and outputs

MMi203: Operator interface

MMi203: Real Time Clock

MMi203: Non-volatile memory

MMi203: OnBoard Counter type A (OBCA)

MMi203: OnBoard Counter type B (OBCB)

MMi203: OnBoard Quadrature Counter (OBQC)

MMi203: Programming

MMi203: CPU device

MMi202: CPU device

MMi99: CPU Device

MMi203: Option links

MMi203: SPice connector

MMi203: Xwire

MMi202: Comms connector

MMi203: Comms connector

MMi203: Communications

MMi203: MultiTrack

MMi203: Operating temperature ratings

MMi203: Firmware revisions and reFlashing


MMixxx: Introduction

This document provides you with detailed technical information on the SPLat MMiXXX programmable controllers. It covers the MMi203, MMi202A, MMi202, MMi201, MMi200, and MMi99. These models are successive generations of the same base design, which has evolved over time starting with the MMi99. The MMi99 is (was) itself evolved out of the MMi88. They all have the same physical size and shape and external connections. The differences are in the size and capacity (memory and speed) of the processor and in the feature sets.

The document has been updated to reflect the most recent design, the MMi203 (first shipped around January 2013). The earlier designs, MMi201, MMi202A, MMi202, MMi200, and MMi99, can be regarded as subsets of the MMi203 and are also covered by implication. Any differences between the MMi203 and the older models are pointed out.

This document is part of the SPLat Knowledge Base (SKB). The SKB is a comprehensive collection of documents covering all technical aspects of the SPLat product. It is published in electronic formats (HTML-Help and HTML) and is also available electronically as a series of document files you can print out yourself. By publishing the documents only in electronic form, we not only save some trees but are also able to make updates available to you very quickly on our website. We suggest you check our Website periodically to ensure you have the most recent version.

Previous Topic

Next Topic

In This Chapter

Getting started with your controller

MMi203: Using this document

MMi203: Product overview

MMi203: Differences from previous models


MMi203: Using this document

This document concerns itself primarily with information that is specific to the MMi20x products. In particular, it concentrates on the hardware aspects of the MMi203. Because programming is uniform across all SPLat products, all SPLat programming information is located elsewhere. For more introductory information on programming please see MMi203 Programming within this document.

The material is organized largely according to various hardware features. This should allow you to quickly locate the information you need in order to properly match the MMi203 to the outside world and make electrical connections. There are also a couple of chapters on more general topics like determining the permissible operating temperature.

We suggest you start with a quick read through the whole document, to familiarize yourself with what's there. After that you should be able to use it as a technical reference to look up specific information you need.

Most technical product manuals contain a Specifications section. We've done it a bit differently (we do many things differently, which is probably why you are using SPLat!). The numbers and ratings are discussed as part of each feature. For example, input voltages and currents are discussed under MMi203 External digital inputs. While this may seem to make the information a little harder to locate (compared to a consolidated table), it gives us the ability to loosen up a little and discuss how the operating envelope can be flexed under different conditions. For example, the allowable output current depends on the operating ambient temperature.


MMi203: Product overview

The MMi203 is a SPLat single board programmable logic controller that includes a number of innovative features. Key features are:

  • 8 digital inputs;
  • 8 digital outputs;
  • 2 versatile analog inputs;
  • 2 analog outputs;
  • Onboard Operator Interface, with support for a Liquid Crystal Display (LCD);
  • Real Time Clock as standard (optional in earlier models)
  • An extremely flexible range of expansion possibilities.

The most unusual feature of the SPLat MMi203 is its built in operator interface. This gives you push buttons, LED indicators and a beeper, plus optional use of a Liquid Crystal Display (LCD), to allow interaction with an operator. It also gives you the ability to tailor the appearance of your end product via a plastic graphical membrane overlay.

The MMi203 is supplied a several different packages. Please refer to our website for the current range of product offerings.


MMi203: Differences from previous models

MMi203 versus MMi202A

  • Nearly 64K of User Flash (program + NVEM page 0), up from 32K.
  • Real Time Clock as standard
  • 12.5% higher internal clock frequency, making it run that much faster (all timing instructions, of course, run at the correct speed).
  • 12.5% faster PWM base frequencies, set via the CPU device
  • Removed the programmable DSR line off the comms connector

MMi202A versus MMi202

The MMi202A brings out the Xwire serial expansion bus to a pair of dedicated 2-pin connectors located close to the power connector, whereas the older MMi202 uses a pin on the SPice connector.

MMi202 versus MMi201

  • The MMi202 uses a newer, faster processor. It runs nearly 2.5 times faster (all timing instructions, of course, run at the correct speed).
  • The analog inputs and outputs are now 10-bit resolution. To take advantage of the added resolution you must use fAnIn and fAnOut instructions.
  • TSP is replaced by Xwire, which allows several SPLat controllers and compatible peripheral boards to be networked.

MMi201 versus MMi200

The primary difference between the MMi201 and the MMi200 is that the MMi201 has an optional Real Time Clock.

The MMi201 also restores the ability to use the OBCA at the same time as analog outputs A and B.

MMi200 versus MMi99

The MMi200 is a major upgrade from the MMi99. The main thing is that it uses a larger processor chip, with more flash memory (60K vs 32K). This allows us to give you much more memory for your programs, and at the same time gives us more memory to add Firmware features. In the months after the initial release of the MMi200 a significant number of new features will be added.

Functional enhancements

  • MMi200 has up to 25K of program memory space versus 9K in the MMi99
  • Up to 16K or NonVolatile Extended Memory onboard. This provides for storage of large end-user alterable lookup tables.
  • The high speed firmware counter (Count7 counter) has been increased from 500Hz to 2kHz. This provides an alternative to the OBCA (see potential incompatibility below).
  • Twice the endurance in permanent memory

Note that with reFlash technology future firmware enhancements can be loaded into existing MMi200 boards.

Potential incompatibility

The new, larger chip has one idiosyncrasy that leads to a potential incompatibility. Because the chip maker re-assigned some pins, the MMi200 cannot operate the onboard counter A (OBCA) at the same time as analog outputs A and B (the normal onboard analog outputs). You can use the analog outputs, or you can use the OBCA, but you can't operate them both in the same program. SPLat/PC versions that support MMi200 detect incompatible usage during simulation and translation, providing you use OBCA_ instructions (it cannot detect low level instructions that address !OBCA).

MMi99 user who needs both capabilities simultaneously should be sure to use MMi201 boards, which do not suffer this limitation.


MMi203: Making external connections

The above picture shows all the connectors used for making external connections to the MMi203 and its predecessors. Moving clockwise from the top these are:

Inputs 0-3 and 4-7

There are two 5-pin connectors for external digital inputs. Each connector has 4 signal pins and one common (ground). The wiring for these is detailed under MMi203 external digital inputs.

Power

This 2-pin connector is for applying power to the MMi203. The wiring and power supply requirements are described under MMi203 Power supply.

SPx

The SPx expansion scheme is documented elsewhere in the SPLat Knowledge Base.

Outputs 0-3 and 4-7

These two 5-pin connectors are for the 8 external outputs. Each connector has the positive power supply pin brought out. The use and wiring of these is detailed under MMi203 external digital outputs.

CAUTION: Take care never to short an output pin to the positive common pin. If the output is on at the time you will blow up the output chip instantly!

Analog out

This 4-pin connector is for the 2 analog outputs. There are 2 signal pins and 2 ground wires. The use and wiring is documented under MMi203 external analog outputs.

Analog in

This 4-pin connector is for the 2 analog inputs. There are 2 signal pins and 2 ground wires. The use and wiring is documented under MMi203 external analog inputs.

SPice

This connector is for low cost SPice add-on boards. Normally you would simply plug a compatible board onto this connector. Some information about the connector is provided under MMi203 SPice connector.

LCD

The LCD connector supports a character Liquid Crystal Display.

Comms

This connector is for communications to a PC either directly or via a modem. The various uses are documented in many places throughout the SPLat Knowledge Base. You will find an overview under MMi203 Comms connector.

Previous Topic

Next Topic

In This Chapter

Some common terminology

MMi203: Where to get matching connectors


MMi203: Where to get matching connectors

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.

The connector sizes you need for the MMi201 are:

Inputs: 2 off 5-way

Outputs: 2 off 5-way

Power: 1 off 2-way

Analog in: 1 off 4-way

Analog out: 1 off 4-way


MMi203: Power supply

The MMi203 needs a suitable DC power supply connected to the 2-pin "PWR" connector. The positive terminal of the power supply must connect to the pin labeled "+" and the negative terminal must connect to the pin labeled "0V". Please see the article on common terminology for an explanation of the labeling.

Supply voltage

In general, the MMi203 needs a supply voltage between 12VDC and 24VDC. The voltage is allowed to go to a maximum of 32V. Note that the MMi203 operating temperature ratings depend on the supply voltage.

It's quite OK to use an unregulated power supply. Just remember that the ripple will make the voltage instantaneously less than the average DC value, and the board will respond to the instantaneous value. Remember, too, that an unregulated supply can go quite a bit higher than its nominal value, especially when lightly loaded and if the mains voltage is high.

If you are not using the analog functions, you can operate the board down to 10VDC. That means it can for example work off a 12V lead-acid battery (though in a vehicle it may not function during starting, when the battery can get dragged down to well below 10V).

The thermistor function contains a programmable drive voltage that can go up to 20V. If you are using that function you must use a supply voltage at least 2V more than the thermistor drive voltage you are going to program.

It is quite possible for add-on boards to impose additional constraints on the supply voltage. Please refer to the add-on board documentation.

Supply current

The current drawn by the board will depend on the supply voltage, what the board is doing and on any add-ons. Allowing an 2.5mA for an LCD and 5mA for a SPice board, maximum supply current draw versus supply voltage is:

Voltage | Max current
10V | 115mA
12V | 125mA
15V | 140mA
18V | 160mA
24V | 200mA
28V | 225mA
32V | 250mA

These figures are calculated based on everything being on at once, including beeper, all indicator LEDs and a full 5mA load on both analog outputs. The figures do not include any external loads (except analog) such as relays.

Note: Figures at 28V and 32V are given for reference only. We do not recommend operating the board at voltages over 24V for sustained periods of time. The board can be damaged if the power supply voltage goes over 32V, even momentarily.


MMi203: External inputs and outputs

Previous Topic

Next Topic

In This Chapter

MMi203: External digital inputs

MMi203: Input response times

MMi203: External digital outputs

MMi203: External analog inputs

MMi203: External analog outputs


MMi203: External digital inputs

The above picture shows the two (digital) input connectors and a couple of typical input devices.

Each connector contains the connections for 4 inputs, plus a common pin that connects onboard to the power supply 0V. The common pin on each input connector is provided simply as a convenience to you when wiring to the board.

In the picture A is a simple switch connected to input 6. B is the output of an NPN style sensor connected to input 0. The sensor would also require a positive supply voltage to power it. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

The input is ON when the input pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - (V(supply) - 2V) / 10000

The leading minus sign simply means the current is flowing out of the pin. The 2V represents the voltage drop of the input indicator LED. The 10000 represents the 10K input pullup resistance.

This equation translates to about 1mA with a 12V supply and 2.2mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognise the input as ON when the voltage on the input pin is typically less than 2.3V (1.5V worst case). That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input, as defined by the processor chip, is approximately 2.3V or a little less. However, the chip manufacturer (Motorola) only guarantees that an input will be treated as low (in our case ON), if the voltage is below 1.5V. Similarly, the chip is guaranteed to treat as high (OFF) only voltages above 3.5V. The range between 1.5V and 3.5V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

Note that the threshold voltage varies from product to product. Don't assume that what applies to this board will apply to others.

The external inputs have a built in hardware noise filter for improved compliance with electromagnetic emission requirements. The input filter introduces a delay in the input response time. The delay times are different for OFF and ON, and vary with the supply voltage. Turn-on time is less than 1µS. Turn-off time is approximately 500µS divided by the supply voltage, i.e. 42µS at 12V and 21µS at 24V.

How to blow up the inputs

You can blow up the inputs by:

  1. Applying a positive voltage 5V above the positive supply. This can damage the indicator LED instantaneously by reverse voltage breakdown;
  2. Applying a negative voltage of more than (32V - The positive supply voltage). This will damage the input pullup resistors due to overheating. This is not a sudden death failure. The pullup resistors are rated at 200mW.


MMi203: Input response times

The external inputs have a built in hardware noise filter for improved compliance with electromagnetic emission requirements. The input filter introduces a delay in the input response time. The delay times are different for OFF and ON, and vary with the supply voltage. Turn-on time is less than 1µS. Turn-off time is approximately 500µS divided by the supply voltage, i.e. 42µS at 12V and 21µS at 24V.


MMi203: External digital outputs

The adjacent picture illustrates the (digital) output connectors and some typical loads.

The 8 outputs are brought out on two 5-pin plug-in connectors. Each connector has a "convenience" pin connected to power supply positive. The output load should always be connected between the output pin and power supply positive. Use the convenience pin in preference to wiring directly to the power supply +.

CAUTION: Take care never to short an output pin to the positive common pin. If the output is on at the time you will blow up the output chip instantly!

The loads illustrated are:

A: The input pins of a solid state relay. Note the polarity!

B: A relay coil. Notice we have included a catch diode across the coil. While there are diodes on the board we recommend a diode across the coil, as close as possible to the coil, to minimize problems with switching noise. A suitable diode is a 1N4001 or similar 1A/50V rectifier diode.

C: An LED, with a series current limiting resistor. Resistance calculators

The (digital) outputs are implemented using the ULN2803A, a chip that contains Darlington transistors. The onboard indicator LEDs are connected directly to the output pin, and returned to the positive supply via (typically) 10K current limiting resistors.

The outputs consist of transistor switches between the output pin and 0V. When the output transistor is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage. If you set the multimeter to 200mADC, the output LED will turn on and you will get a reading of approximately V/10 milliamps, where V is the supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output chip will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built into the chip. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the relay coil as close as possible to the relay, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

If you connect the loads (for example relays) separately to the power supply or to a separate power supply, it may be possible to remove the controller's supply connection and still have power fed to the controller via the loads and the onboard protection diodes. This can produce confusing or possibly even dangerous results if the controller gets enough of this "sneak" power to run. It is safer to use the convenience + supply pin on the connector to provide positive voltage to the load.

The maximum allowable output current is 400mA steady state, with brief (100mS) peaks of 500mA allowed. There is also a limit to how much total current one ULN2803A chip can handle. This relates to the internal heating of the chip.

Let's look at 2 extreme cases:

  1. All 8 outputs on one chip conducting the same current
  2. The allowable current concentrated in a few outputs.

Equal currents

The following table lists allowable current versus ambient temperature, when all 8 outputs are conducting the same current at once:

Temperature | Allowable current per output
25°C | 180mA
40°C | 160mA
50°C | 145mA
60°C | 130mA
70°C | 115mA

Concentrated currents

The following table shows the maximum allowable currents, when concentrated in a few outputs, versus temperature

Temp | 1st O/P | 2nd O/P | 3rd O/P | 4th O/P
25°C | 400mA | 400mA | 300mA | 0
40°C | 400mA | 400mA | 150mA | 0
50°C | 400mA | 400mA | 0 | 0
60°C | 400mA | 325mA | 0 | 0
70°C | 400mA | 220mA | 0 | 0

Naturally there are many other possible combinations, but the above should serve as a guide.

The above figures are intended as a guide only. In any event, the absolute maximum operating temperature for the ULN2803A chip is 70°C.

If you feel like doing the actual sums, here's the procedure.

  1. For each output, calculate its output voltage drop from the formula
    V(out) = 0.92 + 1.85 * I(out) [ I(out) in Amperes ]
  2. For each output calculate the dissipation in watts from the formula
    P=V(out) * I(out)
  3. Add up all the dissipation figures to give P(tot) in watts
  4. Calculate the maximum allowable ambient temperature from the formula
    Ta(max) = 125 - P(tot) * 55
  5. If you get an answer over 70°C then use 70°C

This calculation aims at a maximum internal (junction) temperature of 125°C, which is the maximum rating of the chip. There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better. The ULN2803A data sheet contains additional rating curves.

When semiconductor switches fail they are more likely to become stuck ON than OFF.

Note: This discussion pertains to the maximum operating temperature for the ULN2803A output chip only. See MMi203 Operating temperature ratings for a further discussion.

How to blow up the outputs

Here are some of the ways you can blow up the outputs:

  1. Connecting the output pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than the output current rating)
  2. Connecting the output pin to any negative voltage
  3. Connecting the output pin to any voltage greater than the positive supply.
  4. Switching a load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  5. Using an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.
  6. Driving an incandescent lamp. Incandescent lamps draw a warm-up surge current of many (up to 20) times their rated steady state current.

After you've blown up the outputs

We have thoughtfully fitted the ULN2803A output chip in a socket for you, so you can replace it without soldering. We can supply a 6-pack of ULN2803A chips. Our product code is ULNx6, and is available on our website. Alternatively you can buy the chip wherever fine chips are sold, including many electronics hobbyist stores.. Here are some supplier names and order codes:-

Supplier | Order code
Digikey (USA/International) | Search site
Mouser (USA/International) | 511-E-ULN2803A
Altronics (Australia) | Z3010
Element 14 (formerly Farnell Electronics) (UK, Europe, Australia, NZ) | 705-159, 409-868, 318-7032

The ULN2803A is made by many manufacturers, including Motorola, Texas Instruments, Alegro, Toshiba and ST Microelectronics. You should be able to buy them from most electronic component distributors and even from major electronic hobby stores.

When semiconductor switches fail they are more likely to become stuck ON than OFF.


MMi203: External analog inputs

MMii99 analog inputs

The picture above shows the connector used for analog inputs, and the associated configuration pins. An input is configured by using one of the supplied jumpers to bridge certain pins.

There are two analog input channels on the MMi203 board. Each input can be configured to operate on 0-10V, 0-20mA or as a temperature measuring input (using an external thermistor sensor). In all cases the input uses one input pin and one common pin. The common pins on the analog input connector are tied to the board's 0V terminal. Please see the article on common terminology for an explanation of the 0V terminal.

The two analog inputs are designated as analog C and analog D in AnIn instructions and as 2 and 3 in fAnIn instructions. Each channel has a 4-pin configuration jumper field. A single jumper (shunt) on a pair of jumper pins selects the operating mode (V=voltage, I=current or T=temperature).

Using voltage mode

To use an analog input in voltage mode you set its jumper to bridge the two pins either side of the letter V and apply the input voltage between the input (positive) and the common (most negative). When the AnInC or AnInD instruction is executed, you will get a number between 0 and 255 in the X-register. 0 represents 0V and 255 represents 10V.

From dialect 18 you can also use the fAnIn instruction. This will return a normalized floating point number in W which ranges from 0 at 0V in to 1.000 at 10V in. The range 0 to 1.0 is divided into 1023 steps of 0.00097752 (on older controllers, viz MMi200 and MMi201 the range is divided into 255 equal steps of 0.0016334). fAnIn selects the required channel with a numeric argument, 2, for input C, 3 for input D. The channel number is jndexed.

In voltage mode the input resistance is 200KOhm. The accuracy is about 1.5% of full scale and resolution is 10-bit or 0.1% of full scale (8-bit/0.4% on older models). There is an input noise filter comprising a single RC time constant of 5mS.

If you leave the jumper out, you will get a 0-5V range instead of 0-10V.

Using current mode

To use an analog input in current mode you set its jumper to bridge the two pins either side of the letter I. You then feed the current to be measured into the input pin, with the return out of the common pin. The current source (e.g. 4-20mA transmitter) must be of the type that sources a positive current from its output pin and sinks (returns) the current into its ground (or minus out) pin. Most 4-20mA transmitters are "line powered" and have a simple 2-wire connection. Connect the positive pin of the transmitter to the positive supply voltage and the negative pin to the SPLat input pin.

Some 4-20mA devices are non-loop powered. These have 3 connection wires: Positive supply, ground/negative supply and output. To connect such a device to the SPLat provide it power to its positive and ground pins, connect its ground pin to the SPLat 0V and its output to the SPLat analog input.

When the AnInC or AnInD instruction is executed, you will get a number between 0 and 255 in the X-register. 0 represents 0mA and 255 represents 20mA. At 4mA you will get a reading of 51 (plus or minus any conversion error). You can also use fAnIN, which will return 0 for 0mA, 1.0 for 20mA and 0.2 for 4mA (plus or minus any conversion error). fAnIn selects the required channel with a numeric argument, 2, for input C, 3 for input D. The channel number is jndexed.

In current mode the input resistance is 250Ohms. There is an input noise filter comprising a single RC time constant of 10mS.

Accuracy and resolution (voltage and current modes)

The accuracy is about 1.5% of full scale. Resolution is 10-bit (0.1% of full scale) on MMi203. On older controllers, viz MMi99, MMi200, MMi201 the resolution is 8-bit (0.4% of full scale).

Using thermistor mode

To use the analog input in temperature mode you set its jumper to bridge the two pins either side of the letter T and connect a suitable thermistor between the analog input pin and common. There are two basic types of thermistor: Positive Temperature Coefficient (PTC) and Negative Temperature Coefficient (NTC). The latter, NTC, is the type used for temperature measurement with SPLat. Typically, an NTC thermistor is specified as having a certain resistance at 25 degrees C.

The adjacent diagram shows the thermistor measuring circuit.

MMii99 thermistor circuit

The thermistor is the bottom resistor in a voltage divider. The top resistor is a fixed onboard 10.25KOhm resistor. The voltage divider is driven by a programmable voltage that appears as analog output 8. This is an "onboard output" that is used for thermistor drive and is also available on the SPice connector, but does not "use up" either of the two externally available analog outputs. The thermistor drive voltage can be set to anything between 0V and 20V. Note that the board supply voltage must be at least 2V greater than the intended drive voltage. The drive voltage is programmed using an fAnOut 8 instruction. With W=0 this will result in 0V out, with W=1 it will result in 20V out.

In this thermistor mode the full scale analog input voltage is 5V.

A thermistor is an electronic component that changes resistance with temperature. Don't confuse this with an RTD sensor, which also changes resistance with temperature but is much less sensitive! The warmer an NTC thermistor gets, the lower its resistance. Hence, a reading of the input voltage can be used to calculate the thermistor temperature.

Accuracy and resolution depend on the thermistor you use and how you program it. The best achievable figures are about 0.2°C for both, if the system includes provision for software calibration. The SPLat Knowledge Base contains a tutorial on thermistor temperature measurement using SPLat boards. As a first approximation, when shopping for a thermistor for your application, shoot for one with a resistance of 10KOhm in the middle of the temperature range you need to cover.

How to blow up the analog input

Here are some ways of blowing up the analog inputs:

  1. In any mode, connect the input to a voltage greater than the positive supply or negative with respect to the 0V terminal.
  2. In current mode, apply more than 7.5V to the input. This will overheat the current shunt resistor and lead to loss of accuracy or, in the extreme, damage the board.

Analog inputs and SPLatLink

The board is capable of returning analog readings via SPLatLink without any specific provisions on your part.


MMi203: External analog outputs

MMii99 analog outputs

The above picture shows the connections for the two external analog outputs. They are designated analog outputs A and B. Each output can generate a 0-10V signal, with a maximum load current of 5mA, corresponding to a 2KOhm load. The outputs have internal current limiting (which is not the same as being short circuit proof!).

The analog output voltage is a positive voltage measured with respect to the 0V terminal. It is programmed using the AnOutA or AnOutB instruction. With X=0 this will result in 0V out, with X=255 it will result in 10V out.

The output resolution is 8 bits (256 steps between 0 and full scale) when you use the AnOut# instructions and 10 bits when you use fAnOut. The output is generated as a filtered pulse width modulated (PWM) signal. The filter consists of a simple 100mS RC low pass filter. For a full scale step output change, say from 0V to 10V, this will take 230mS to settle to within 10% of the full change, 450mS to settle to 1% and 550mS to settle to 0.4%.

NOTE: In the MMi200 the OBCA cannot be used in the same program as Analog outputs A and B. If you attempt to do so you will get a runtime error. Analog outputs I and J (thermistor drive and SPice connector) are not affected.

This does not affect the MMi99, MMi201 or MMi203.

How to blow up the analog outputs

Here are some ways of blowing up the analog outputs:

  1. Connect the output to any positive voltage over 10V and program a 0V output.
  2. Short the output to 0V for extended periods of time (it may survive)
  3. Connect the output to a negative voltage, even momentarily.


MMi203: Operator interface

The MMi203 is named for its built in operator interface (Man-Machine interface). The operator interface consists of:

5 push buttons

These are miniature momentary action push buttons, wired to dedicated inputs. They are sensed by a program just like any other input, though the SPLat programming language contains special instructions that are designed to simplify programming user interface operations.

7 LEDs

These are program-controlled front panel indicators. They are programmed like any other output. There is one pair of instructions for making it extremely easy to make them blink (flash).

Beeper

Allows audible alerts and alarms

Graphic overlay

This is a flexible plastic sheet that covers the front panel. It contains the labels and marking required to inform your user about what the various controls are for. If you have your own made you can also include your own company logo.

Liquid Crystal Display (optional)

The LCD extends the capability of the operator interface, allowing you to create numeric and message displays and user-friendly menus.

MMii99 2x16 overlay with i-o numbers

In the adjacent picture the push buttons and LEDs have been labeled with their input (buttons) and output (LEDs) numbers.

Note: MMi99 Firmware versions prior to 3.13 contained a bug that leads to the order of the front panel LEDs being reversed, relative to the illustration, in some board revisions.

Previous Topic

Next Topic

In This Chapter

MMi203: Front panel push buttons

MMi203: Front panel LEDs

MMi203: Liquid crystal display

MMi203: Beeper

MMi203: Graphic overlays

MMi203: Face plate


MMi203: Front panel push buttons

The front panel push buttons give you the ability to get operator inputs without having to wire in any external controls. They are programmed as inputs 8 through 12. The input will be seen by your program as ON (True) when the button is pressed.


MMi203: Front panel LEDs

The front panel LEDs are programmed as outputs 9 through 15. The LED will illuminated when your program sets the output ON.

There is a special instruction, Blink, that causes the LED to flash. There is also an instruction called OutputB which blinks the LED if X is true and turns it off if X is false. By default the flash pattern is 400mS on and 400mS off. The instruction BlinkMask can modify the blink pattern (for all outputs at once).

Note: MMi99 Firmware versions prior to 3.13 contained a bug that leads to the order of the front panel LEDs being reversed, relative to the illustration, in some board revisions.


MMi203: Liquid crystal display

The MMi203 board has a connector for a character LCD. The MMi203cDK216 comes complete with a 2 line by 16 character LCD.

The LCD is fully documented in the SPLat Knowledge Base document "Onboard LCD documentation"

Warning: The board and the metal faceplate (or any other metallic surface that the board is mounted to) must be attached using metal screws and no insulating hardware so they are at the same potential. The mounting holes on the board are deliberately made with exposed metal. Failure to "bond" the face plate to the board will expose the board and the LCD to the risk of damage from static discharge.


MMi203: Beeper

The beeper appears as a simple output on output 8. You can program it using normal ON, OFF, Output and Blink instructions.

The beeper is automatically sounded briefly whenever one of the pushbuttons is read as true with an InputK instruction. The KBeepOff instruction allows you to suppress the sound. The KBeep instruction lets you generate an identical brief beep.


MMi203: Graphic overlays

You will find dimensioned drawings of our universal graphics overlay on the Resource Kit CD under the File Resources folder. They are there in several formats. You can use those as a starting point for creating your own labels. Please contact us if none of the formats there are suitable for you, and we will try and generate a mutually agreed format (if we have the right software to do so). MMii99 panel cutaway

The picture illustrates how the universal overlay supplied with -DK (Developer's Kit) versions works. The overlay has clear windows placed next to the switches and buttons, plus one larger window (not shown). The idea is that you make yourself a self-adhesive paper label on an inkjet or laser printer, stick that onto the metal panel and then stick the plastic graphics overlay down on top. Make the paper label just large enough and no larger, so the plastic overlay seals it all around.

If you are going on to make (or have made) your custom overlays, you will need to design your own artwork. Unless you have already been through that process, you are well advised to start by first locating a screen printing company to do your printing and consulting with them on suitable artwork formats. Don't do the artwork first and then look around for a printer ... you will most likely find you have been wasting your time.

Another word of caution: If you use a graphic artist to design a snazzy overlay, watch out for two things:

  1. Graphic artists tend to ignore considerations of cost. More colours equals higher cost
  2. Some graphics artists have no idea about making dimensionally accurate artwork.

Some companies are now offering a service making prototype overlays for a very reasonable price.

It is also possible to make quite good looking overlays using an ordinary colour inkjet printer. You can print on paper and get it laminated, or rear print on transparency film. Either way it helps to get hold of a supply of double-sided adhesive film. If you make your own for prototypes keep in mind that a subsequent production screen print can only have solid blocks of colour (spot colours), not blended and graduated colours (process colours).

If you are making a large number of custom overlays, consider having domes heat-embossed over the push button switches. This will look better and also reduces the tolerancing requirements of the board to panel spacing.


MMi203: Face plate

You will find dimensioned drawings of our metal face plate on the Resource Kit CD under the File Resources folder. You can use that to design your own panel.

The spacing between the board and the face plate is super-critical, unless you are using a custom graphics overlay with embossed buttons. If the board is too far off the face plate, it will be difficult or impossible to depress the overlay enough to activate the buttons. If the board is too close to the face plate the buttons will press permanently against the overlay and be accidentally activated. The correct spacer length is 9.5mm less the face plate thickness. Our standard face plates are 1.5mm aluminium, and we use 8mm spacers. We use nylon spacers. These are slightly compressible, and allow some fine adjustment of the spacing by adjusting the torque on the mounting screws.

Warning: The board and the metal faceplate (or any other metallic surface that the board is mounted to) must be attached using metal screws and no insulating hardware so they are at the same potential. The mounting holes on the board are deliberately made with exposed metal. Failure to "bond" the face plate to the board will expose the board and the LCD to the risk of damage from static discharge.


MMi203: Real Time Clock

The Real Time Clock (RTC) is an option offered on certain versions of the MMi203. It has the following features:

  • Time of day (24 hour clock) and date functions.
  • 16 presettable/programmable events for triggering program actions. These are programmed as events 0 to 15.
  • A super-capacitor based backup power source that will keep the clock running for up to 2 days on loss of main power. Unlike batteries the super-capacitor will never wear out.
  • A connection point for a user-supplied battery, in case you need more than 2 days' backup.

Connecting an external backup battery

If you need more than the 2 days' backup power provided for the RTC by the onboard super-capacitor, you can provide your own battery. Any battery voltage between 3V and 5V will work. Do not exceed 5.5V. We suggest a 3V lithium or two alkaline AA cells. Do not use rechargeable batteries as the circuit will not charge them. The battery should be connected to the small 2-pin connector near the Comms connector. Make sure you get the polarity right. The RTC chip draws less than 2uA in backup mode, so any battery will last a very long time.

Memory wear-out

The memory used to store RTC events is limited to 100,000 writes per event. After that it may become unreliable. So make sure you don't write a program that does very many RTCWriteEvent instructions.

RTC programming

Full details on programming the RTC are in the RTC documentation.


MMi203: Non-volatile memory

The term non-volatile memory means a form of memory that does not lose its data when the power goes off. It can be implemented using a variety of technologies. Your hard disk drive is non-volatile. The magnetic core memory used in computers in the 60's and 70's was non-volatile. Some systems use a small battery to keep the memory powered up even when main power is off. The technologies commonly used in SPLat boards are known as "EEPROM" and "FLASH" memory.

The controller implements three kinds of non-volatile memory: "Permanent" memory, "Shadow" memory and "Non-Volatile Extended Memory" (NVEM). The Permanent memory has existed in SPLat boards almost from the very beginning. Shadow memory is a much newer development made possible by advances in microcontroller technology. NVEM is even more recent, and was introduced into off the shelf products in dialect 16.

Permanent memory

This type of non-volatile memory was introduced into SPLat in March 1998. It allows just 13 bytes of RAM (data memory) to be saved into a non-volatile memory as a indivisible (all or nothing) block to be later restored. The two instructions for this are PermStore and PermRecall.

Shadow memory

This is newer type of non-volatile memory, introduced in March 2002 in language dialect 10. It allows all of RAM to be selectively backed up to a "shadow" memory in blocks of any size, and likewise to be restored in blocks of any size. It also allows the shadow memory to be cleared in blocks, and supports indexed and jndexed addressing for more sophisticated data management in larger projects.

The term shadow memory comes about because it is a region of non-volatile memory that overlaps the RAM. When a certain RAM location is written to the shadow memory, it is written to the same byte address within the shadow memory as its own RAM address.

The instructions for shadow memory are ShadowWrite, ShadowRead and ShadowClear. All these can be used as indexed as well as non-indexed instructions.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

NVEM page 0

The controller implements "NVEM page 0", which is a non-volatile memory that makes available any of the User Flash memory in the processor chip that is not used by your program.

Comparison between nonvolatile memory types

The following table compares the three nonvolatile memory types as implemented in most SPLat controllers. Actual figures vary between specific products. Make sure you read the descriptions of each type for your product.

Type | Size | Typical use | Endurance (in typical product) | Notes
Perm-mem | 13 bytes | Cycle counters, hour meters | 500K to 1M | Built in error detection. Can only be written and read as a single block. Does NOT survive downloading of a new program
Shadow | Same size as RAM | User configuration data | 10,000 total (not per byte!) | Very easy to use, works very well in menu systems. Can be read and written one byte at a time. Survives downloading of a new program
NVEM page 0 | However much of User Flash is not used by your program. Maximum 32K. | Lookup tables, infrequently changed configuration or pattern data, scripts | 10,000 per block. | Read often/write very infrequently. Writing is constrained by 512 (typical) byte write block size. Must be presumed corrupted if a new program is downloaded.

Endurance of non-volatile memories

Certain non-volatile memory technologies have a finite life, as defined by the number of writes or erases (clear cycles) they can endure. For permanent memory this translates to the total number of times a PermStore can be executed before the memory is likely to give erroneous data. For shadow memory it means the total number of ShadowWrite or ShadowClear instructions before it is likely to give errors. For NVEM0 it is the total of writes to any block of (typically) 512 bytes.

(In older products shadow memory was genuine byte-writable EEPROM, and the endurance was per byte. More recent models use main FLASH because the newer chips lack EEPROM, and each write entails re-writing the whole shadow block. Hence the endurance is now total writes to the block).

You should take great care to not write a program that will write or clear the non-volatile memory so often that it gets worn out too soon.

Example: Say a given non-volatile memory has a stated endurance of 100,000 cycles, and your program counts cans of sardines at a rate of 100 per minute, updating the non-volatile memory after every count. Your board would only last 1000 minutes, or about 2 shifts. If you re-write your program so it only updates the non-volatile memory once per hour, you would get 100,000 hours or about 12 years life, with some loss of accuracy as the trade-off.

CAUTION: You must take into account the possibility of power failing right in the middle of writing to the non-volatile memory. Unless specifically stated for the SPLat product you are using, there is always a possibility that data will be corrupted if that happen. You must therefore ensure, by the design of your system, that there will be no disastrous consequences of corrupted data.

The Permanent Memory has the best protection in this regard. Data is written with a checksum, and the PermRecall instruction provides a result indicator in the R register. This means that in the very least your program can have a good probability of avoiding the use of suspect data.

In the case of writing to NVEM page 0, the risk can be even worse. NVEM page zero follows directly on from the program memory. That means you could be writing to the same (typically 512 byte) block as the end of your program or read-only data tables. A power failure during a write would result in your program getting corrupted. You should therefore have an unused areas of 512 bytes between the program or read-only tables and the part of NVEM page 0 that is to be written to.

Caution: Exceeding the endurance of FLASH memory may damage the controller. It will also void the product warranty.

Support in SPLat/PC

All types of non-volatile memory are supported in SPLat/PC.

The support for permanent memory is documented in the SPLat/PC "classic" help file. In the help file index search for "Permanent memory".

SPLat/PC simulates shadow memory by saving the shadow data in the computer's memory. The data is initialized to all zero's when SPLat/PC is first launched. After that it is affected only by simulated shadow memory instructions. That means you can stop and restart a simulation and the shadow memory data is retained. There is no simulation of an unsuccessful ShadowWrite or ShadowClear.

SPLat/PC support for NVEM includes a separate window to represent the registers and data. The NVEM documentation has more details.

Previous Topic

Next Topic

In This Chapter

MMi203: Permanent memory

MMi203: Shadow memory

MMi203: NVEM0


MMi203: Permanent memory

Permanent memory in the MMi203 is implemented in the main FLASH memory. By means of a proprietary circular re-use technique we achieve an endurance of 1.2 million (640,000 in MMi99) write cycles. The write and read functions are both very fast. The instructions that access permanent memory are PermStore and PermRecall.

Data in Permanent memory is reset to all zeros when a program is downloaded to the board.


MMi203: Shadow memory

MMi99 controllers with Firmware versions 3.3 or earlier contain language dialect 9, and do not implement shadow memory. Later versions of MMi99 firmware, and all versions of MMi203, MMi201 and MMi200, implement shadow memory.

Writing shadow memory in MMi203

Shadow memory in the MMi203 is implemented using main Flash memory. Endurance is 10,000 writes or clears in total. ShadowWrite takes up to 20mS per byte. ShadowClear takes up to 10mS per byte. During these times the SPLat processor can do nothing else (for example quadrature counting). We use a smart algorithm to avoid redundant writes and clears, making the process as fast as possible and reducing wear on the memory.

Writing shadow memory in MMi201 (legacy information)

Shadow memory in the MMi201 is implemented using a byte erasable EEPROM memory. Endurance is 10,000 writes or clears in any one byte. ShadowWrite takes up to 20mS per byte. ShadowClear takes up to 10mS per byte. During these times the SPLat processor can do nothing else (for example quadrature counting). We use a smart algorithm to avoid redundant writes and clears, making the process as fast as possible and reducing wear on the memory.

ShadowRead is very fast.

Downloading a program to the controller does not change the contents of shadow memory.


MMi203: NVEM0

NVEM0 (page 0 NVEM) in the MMi203 is part of the same memory where your application program is stored. In fact, your program and NVEM0 share the same contiguous block of memory, called User Flash. The bigger your program, the less is available for NVEM0, and visa versa. In the MMi203 there is approximately 21K of user flash. The exact amount is reported in the SPLat/PC Module window when you connect to a board. The amount may reduce in future Firmware versions as we use more of the total flash memory to implement new features.

In the following description for the MMi203, the block size is 512 bytes and the endurance is 10,000 writes (this varies from product to product). For the MMi201, the block size is 128 bytes and the endurance is 10,000 writes (this varies from product to product).

The endurance figures given above are the chip maker's worst case ratings over all legal temperatures and supply voltages. They are very conservative but no guarantee is given that the chips can survive more writes. Exceeding the FLASH memory write endurance will be construed as mis-use and void our product warranty.

Boards with language dialect 15 or later implement NVEM0. This page provide details specific to the implementation in the SL99/100, MMi2xx, MS12x, CC16/18, RFD18, and HD8. General NVEM0 documentation is located elsewhere.

NVEM0 in these controllers is implemented using the processor's main FLASH memory. This memory can only be written in fixed size blocks. The block size differs between products.

What we have done, therefore is to create some "smoke and mirrors" to simulate byte-writable memory. That means your program can write to individual bytes, and they will be updated correctly. However, there are downsides:

  • Every time you write one byte all the bytes in the same block must also be written. That has a serious consequence for the endurance of the memory. Endurance refers to the unavoidable fact that FLASH memory wears out after a certain number of writes. What this means is that you must be very careful when using NVEM0 not to write to it too often.

    Our Firmware is smart enough to know that several bytes being written from one instruction only requires one single block write. Hence, writing a multi-byte record or a floating point number only counts as one "hit" on the endurance, but it does hit every byte in the block. But so does writing one single byte!
  • Because the processor must write a whole block at a time (actually, erase and then re-write the whole block), writes to NVEM0 are quite slow. Also, there is no way you can know where the block boundaries are relative to your program, so any write operation involving more than 1 byte could cross a boundary and therefore require 2 writes.

Whenever you change you program, and hence its size, the absolute addresses of all your NVEM0 data will change. That means a particular multibyte write operation could suddenly wind up straddling a block boundary and take twice as long. Be very cautious when evaluating the effect of write times on your program.

  • NVEM0 is located in the processor's Flash memory, directly after your program. There is a big danger in reading, erasing, modifying and re-writing the block that contains the end of your program and/or any block containing read-only tables. If the power should fail during the write you will not only lose your data, you will also damage your program (or tables) and most likely wreck your product. For this reason this NVEM0 implementation will not permit writing to the memory block that contains the end of your program. If that is attempted the board will suffer a fatal runtime error. You should therefore make sure that there are at least one block's worth bytes of not-written-to NVEM immediately before any NVEM0 that gets written to by your program. The best policy is to place all read-only tables directly after you program, then to pad out with one block of unused NVEM0.

Examples:

;Example 1: Padding with unused space, because I have no read-only tables:

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM page 0 memory

           NV0Space     XXXXX      ;Force new XXXXX byte block

NVData     ;Start defining any writable NVEM0 areas here

********* ********* ********* ********* ********* ********* ********* *********

;Example 2: Place read-only lookup tables first, then force a new byte block.

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM

;Table of sines 0-90', 10' increments (takes 40 bytes)

SineTable  NV0fNum      0          ; sin(0)

           NV0fNum      1.74E-1    ; sin(10)

           NV0fNum      3.42E-1    ; sin(20)

           NV0fNum      5E-1       ; sin(30)

           NV0fNum      6.43E-1    ; sin(40)

           NV0fNum      7.66E-1    ; sin(50)

           NV0fNum      8.66E-1    ; sin(60)

           NV0fNum      9.40E-1    ; sin(70)

           NV0fNum      9.85E-1    ; sin(80)

           NV0fNum      1          ; sin(90)

Padding    NV0Space     XXXXX      ;Force new block

;Any NVEM that gets written during program execution gets placed below this point.

;That would typically be large parameter tables that can be changed by an end-user.


MMi203: OnBoard Counter type A (OBCA)

The board incorporates an onboard counter type A (called OBCA). This is a high speed hardware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or wind speed using a rotating vane anemometer.

The OBCA exists on several SPLat boards. Generic documentation for the feature is provided in the Onboard counter type A guide.

The OBCA is connected to input number 7. There is some input filtering that limits the maximum count frequency. The maximum count frequency, assuming a squarewave input, depends on the supply voltage to the board. At 10V it is 7kHz, and it increases with supply voltage to 18kHz at 24V supply. The accuracy is approximately 0.1% (this varies from product to product, depending on whether they use a crystal for timing or not. This board has a crystal).

NOTE: In the MMi200 (only) the OBCA cannot be used in the same program as Analog outputs A and B. If you attempt to do so you will get a runtime error. Analog outputs I and J (thermistor drive and SPice connector) are not affected.


MMi203: OnBoard Counter type B (OBCB)

The board incorporates an onboard counter type B (called OBCB). This is a medium speed Firmware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or wind speed using a rotating vane anemometer.

The OBCB exists on several SPLat boards. Generic documentation for the feature is provided in the OnBoard Counter type B guide.

The OBCB is implemented on inputs 0 though 7. The maximum count frequency is approximately 1.8kHz, assuming a perfect square wave input. The accuracy is approximately 0.1% (this varies from product to product, depending on whether they use a crystal for timing or not. This board has a crystal).


MMi203: OnBoard Quadrature Counter (OBQC)

The MMi203 implements an OnBoard Quadrature Counter (OBQC) on inputs 0 and 1. A quadrature counter decodes the dual channel outputs of a quadrature encoder and maintains an up/down counter. A quadrature encoder is a (usually rotary) encoder that produces two output signal that indicate the amount of rotations and the direction of rotation.

You should make sure that the encoder cannot generate input state changes more often that 400uS apart. You should also make sure your program is designed to never perform any writes to non-volatile memory when it must track an encoder with the OBQC.

For more information see the generic OBQC documentation.


MMi203: Programming

The MMi203 is a member of the SPLat family of controllers. That means that it shares its programming language and programming tools with all other SPLat controllers. Whatever you learn on the MMi203 is applicable to other models. The only differences with be those relating to particular hardware resources and features, and dialect differences. "Dialect" is a term we use to describe differences between versions of the SPLat language, which really means that we are continually adding new language features which are not available on older controller products.

You program the board by writing and debugging a program to suit your application in your PC. You then download the program to the board and complete debugging and overall system testing. Once the program has been downloaded to a SPLat board it will remain secure in the board. You can download a new program to the board at any time, in which case the old one is overwritten. You cannot upload a program from a board back into your computer, so it is impossible for a competitor to steal your program out of a board.

The PC software you use for programing is called SPLat/PC. SPLat/PC gives you a wide range of programming and debugging tools. It incorporates a help file with reference material and our classic, highly acclaimed, programming tutorial. You can download SPLat/PC free from here.

SPLat/PC also features a series of semi-interactive "mini-tutorials". These are designed to give you a bit of a kick start with SPLat. You should start your exploration there.

The proprietary SPLat programming language contains a repertoire of over 400 instructions. These instructions cover a huge range of functions. The instruction set differs from conventional programming languages and microprocessor assembler languages in that it has a strong emphasis on the needs of control applications. In particular, there is a subset of just 14 instructions called FastTrack. The FastTrack subset contains all you may ever need in order to implement simple timing and sequencing applications with batch counting and multiple interlocks.

In December 2005 we introduced MultiTrack programming. This is an extremely easy method of writing programs that can (seemingly) do several things at once. (For experts: MultiTrack is a non-preemptive multitasking scheduler that is extremely easy to learn and use). MultiTrack extends FastTrack, so that with just 16 instructions you can write useful multitasking programs. The mini-tutorials in SPLat/PC include an introduction to MultiTrack. It has been our experience over many years that a lot of people shy away from MultiTrack because multitasking is a large conceptual leap. The MultiTrack mini-tutorial is excellent in that regard, as it presents the concept in an extremely approachable way. Even if it takes you several hours to wrap your head around, invest the time! The investment will pay off handsomely in anything but the most trivial program. After you have grasped the basics of MultiTrack, your next port of call should be the more detailed MultiTrack tutorial.

The 400+ instructions that comprise the full SPLat programming language are documented within the SKB in the Programming Reference. Some of these are instructions we have introduced to simplify common control tasks even further, such as blinking an LED, and form an extension of the FastTrack concept. Therefore, it's a good idea to look there once you move on from pure FastTrack.

If you need even more knowledge you can work with the tutorial built into the SPLat/PC help file. It starts with a FastTrack tutorial that approaches FastTrack that is different from the approach taken in the online course (this is older material), then goes into the "classic" tutorial. The classic tutorial starts off with the nitty gritty details of the internal registers in SPLat. It then works up from there with a detailed expose, along with many examples, of the vast majority of SPLat instructions and how they are used.

You can also program this controller in C. Click here for details.

Previous Topic

Next Topic

In This Chapter

MMi203: Programming cable


MMi203: Programming cable

The controller needs to be connected to a PC for program download. The full process of connecting to the PC is described in the help file that comes with SPLat/PC, our programming software.

The connection cable you will need is a simple serial cable. You can buy these in most computer stores. If you make your own you need the following wiring (these pinouts are for DB9 connectors):

Controller | Computer
Pin 2 | Pin 2
Pin 3 | Pin 3
Pin 5 | Pin 5

You also need a computer with a serial (COM) port. Modern notebook computers tend not to have COM ports. In that case you will need a USB to Serial adaptor. Not all such adaptors work equally well. The ones we find work are based on the FTDI chipset, and are for sale on our website.


MMi203: CPU device

The MMi203 can respond to UV instructions that address the CPU device. Some of the responses are dependent on the hardware configuration.

Board Type

The board will respond to the following board-type poll:

  SPxPoll2      3,!CPU

by placing its board type identification number on U0 and U1. The response is a function of the type (model) of board and the number of connected XPice expansion boards (e.g. XBIO16, XIRO16, HIO16), FP16. The numbers can be fetched from U0 and U1 by the PushU instruction.

The following table defines the responses for the MMi203, MMi202, MMi201and MMi200 with varying amounts of expansion:

Configuration | U0 | U1; (MMi202/3) | U1; (MMi201) | U1; (MMi200)
MMi20x | 0 | 76 | 64 | 53
MMi20x+16XPice | 0 | 77 | 65 | 54
MMi20x+32XPice | 0 | 78 | 66 | 55
MMi20x+48XPice | 0 | 79 | 67 | 56
MMi20x+64XPice | 0 | 80 | 68 | 57

Interpreting the number of XPice points can be a little tricky. For boards with segregated inputs and outputs, like XIRO16 and HIO16, each 8I/8O board corresponds to 16 XPice points. For the XBIO16, each bidirectional point corresponds to one input and one output (and appears as such on the SPLat/PC screen). That means one XBIO16 board gets counted as 32 XPice points although it only adds 16 I/O points to your configuration.

Total I/O capacity

The instruction

SPxChIn        0,!CPU

will return the following information in U, where it can be accessed with the PushU instruction:

U0 | Number of digital inputs
U1 | Number of digital outputs
U2 | Number of analog inputs
U3 | Number of analog outputs

As with the board type information, interpreting the number of XPice points can be a little tricky. The safest way to use this information is to differentiate between known configurations of your product. For example, you could sell a product with an optional feature that is controlled through an expansion board that may or may not be present.

Data Set Ready line

The MMi203 does not provide a switchable line on the DB9 connector

PWM base frequency

The analog outputs on the board are generated by a pulse width modulated digital pulse train which is filtered (averaged) by an analog filter to produce a DC output. Normally the frequency of the PWM pulse train is 18.432kHz. The base frequency can however be altered using the instruction:

SPxCmd1          0,!CPU

with register U0 preset to a value between 0 and 7. The resulting PWM frequencies are given in the following table:

U0 | PWM frequency
0 | 18kHz (Default)
1 | 9kHz
2 | 4.5kHz
3 | 2.25kHz
4 | 1125Hz
5 | 562.5Hz
6 | 281.25Hz
7 | 140.625Hz

Any other value will result in a fatal runtime error.

All analog output pins are affected. This feature is designed to be used with PWM output circuits such as DC motor speed controls. Be aware that a low PWM frequency will have an impact on any actual analog (filtered DC) outputs. Our filter circuits are normally designed for the default PWM frequency. Using a much lower frequency will produce ripple in the DC output.

When used with an AnOut instruction, the resolution is 8 bits. With fAnOut it is 10 bits.

Modify SuperTimer 10mS clock interval

You can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.

CPU temperature readout

An SPxPoll4 0,!CPU returns the floating point temperature of the CPU chip in °C at locations U(0) --> U(3)

An SPxPoll4 1,!CPU returns the floating point temperature of the CPU chip in °F at locations U(0) --> U(3)


MMi202: CPU device

Note: This applies to the MMi202

The MMi202 can respond to UV instructions that address the CPU device. Some of the responses are dependent on the hardware configuration.

Board Type

The board will respond to the following board-type poll:

  SPxPoll2      3,!CPU

by placing its board type identification number on U0 and U1. The response is a function of the type (model) of board and the number of connected XPice expansion boards (e.g. XBIO16, XIRO16, HIO16), FP16. The numbers can be fetched from U0 and U1 by the PushU instruction.

The following table defines the responses for the MMi203, MMi201and MMi200 with varying amounts of expansion:

Configuration | U0 | U1; (MMi202/3) | U1; (MMi201) | U1; (MMi200)
MMi20x | 0 | 76 | 64 | 53
MMi20x+16XPice | 0 | 77 | 65 | 54
MMi20x+32XPice | 0 | 78 | 66 | 55
MMi20x+48XPice | 0 | 79 | 67 | 56
MMi20x+64XPice | 0 | 80 | 68 | 57

Interpreting the number of XPice points can be a little tricky. For boards with segregated inputs and outputs, like XIRO16 and HIO16, each 8I/8O board corresponds to 16 XPice points. For the XBIO16, each bidirectional point corresponds to one input and one output (and appears as such on the SPLat/PC screen). That means one XBIO16 board gets counted as 32 XPice points although it only adds 16 I/O points to your configuration.

Total I/O capacity

The instruction

SPxChIn        0,!CPU

will return the following information in U, where it can be accessed with the PushU instruction:

U0 | Number of digital inputs
U1 | Number of digital outputs
U2 | Number of analog inputs
U3 | Number of analog outputs

As with the board type information, interpreting the number of XPice points can be a little tricky. The safest way to use this information is to differentiate between known configurations of your product. For example, you could sell a product with an optional feature that is controlled through an expansion board that may or may not be present.

Data Set Ready line

The MMi202contains a Data Set Ready (DSR) line on its communications interface.

The instruction

SPxCmd0        1,!CPU

will turn on (raise) DSR

The instruction

SPxCmd0        0,!CPU

will turn off (drop) DRS

The MMi203 does not have this pin

Earlier versions of this page referred to this pin as RTS. Because the female comms connector is pinned as a DCE (Data Communication Equipment) the correct name is Data Set Ready (RTS is an input on a DCE (aka modem)

PWM base frequency (Firmware V3.15 or later)

The analog outputs on the board are generated by a pulse width modulated digital pulse train which is filtered (averaged) by an analog filter to produce a DC output. Normally the frequency of the PWM pulse train is 16kHz. The base frequency can however be altered using the instruction:

SPxCmd1          0,!CPU

with register U0 preset to a value between 0 and 7. The resulting PWM frequencies are given in the following table:

U0 | PWM frequency
0 | 16kHz (Default)
1 | 8kHz
2 | 4kHz
3 | 2kHz
4 | 1kHz
5 | 500Hz
6 | 250Hz
7 | 125Hz

Any other value will result in a fatal runtime error.

All analog output pins are affected. This feature is designed to be used with PWM output circuits such as DC motor speed controls. Be aware that a low PWM frequency will have an impact on any actual analog (filtered DC) outputs. Our filter circuits are normally designed for the default PWM frequency. Using a much lower frequency will produce ripple in the DC output.

When used with an AnOut instruction, the resolution is 8 bits. With fAnOut it is 10 bits.

Modify SuperTimer 10mS clock interval (Dialect 20 or later)

You can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.

LCD backlight dimming control

The following applies only to MMi203 boards connected to a special dimmable LCD and fitted with V3.21 or later Firmware.

The following is old, and included for reference, or very special circumstances. The OBLCD_Dim instruction provides an easier way of controlling dimming.

The board/Firmware supports a timer that automatically dims the backlight after a set time interval with no user activity, as determined by presses of the front panel push buttons.

The code:

SetU 0,BL_HighLevel

SetU 1,BL_LowLevel

SPxCmd2 1,!CPU

will set the maximum, undimmed (BL_HighLevel) and minimum, dimmed (BL_LowLevel) brightness levels of the backlight. The two levels must be in the range 0 (minimum) to 8 (maximum). Any other values will produce an error.

The code:

SetU 0,BL_DimTime

SPxCmd1 4,!CPU

Will set the auto-dim timer value to BL_DimTime minutes. The allowable range is 0 to 255 (4h15m). Using 0 dims the backlight instantly.

Example: LCD backlight auto-off timer.

CPU temperature readout Dialect 21 or later

An SPxPoll4 0,!CPU returns the floating point temperature of the CPU chip in °C at locations U(0) --> U(3)

An SPxPoll4 1,!CPU returns the floating point temperature of the CPU chip in °F at locations U(0) --> U(3)


MMi99: CPU Device

The MMi99 with late Firmware revisions can respond to UV instructions that address the CPU device. Some of the responses are dependent on the hardware configuration.

Board Type

The board will respond to the following board-type poll:

  SPxPoll2      3,!CPU

by placing its board type identification number on U0 and U1. The response is a function of the type (model) of board and the number of connected XPice expansion boards (e.g. XBIO16, XIRO16, HIO16). The numbers can be fetched from U0 and U1 by the PushU instruction.

The following table defines the responses for the MMi99 with varying amounts of expansion:

Configuration | U0 | U1
MMi99 | 0 | 11
MMi99+16XPice | 0 | 12
MMi99+32XPice | 0 | 13
MMi99+48XPice | 0 | 14
MMi99+64XPice | 0 | 15

Interpreting the number of XPice points can be a little tricky. For boards with segregated inputs and outputs, like XIRO16 and HIO16, each 8I/8O board corresponds to 16 XPice points. For the XBIO16, each bidirectional point corresponds to one input and one output (and appears as such on the SPLat/PC screen). That means one XBIO16 board gets counted as 32 XPice points although it only adds 16 I/O points to your configuration.


MMi203: Option links

There are two option links on the MMi203 board that can be read as inputs 13 and 14. You can use these in your program to select alternative operating modes or other end-user options.

MMii99 option links

On the board they are labeled IN13 and IN14. The jumpers go in the direction illustrated at IN13.

Inputs 13 and 14 are also used on the SPice connector. That means the option jumper function may not be available if there is a SPice board present. In that case you must remove the jumpers before installing the SPice board.


MMi203: SPice connector

This section describes the specifics of the SPice connector on the MMi203. The SPice expansion scheme in general is documented elsewhere in the SPLat Knowledge Base, under the SPLat Expansion Framework.

The SPice connector is the longer of the two single pin-row connectors located away from the edge of the board. It is labeled "SPICE". It is identical to the SPice connector on the SL99, except the pins map to different I/Os.

The SPice connector signals are as follows:

Pin | As input | As output | As AnIn | As AnOut | U(n) | Comments
1 | Raw positive board supply voltage
2 | +5V regulated. Permissible load 5mA
3 | Negative bias voltage. Permissible load 1mA.
4 | 15 | 16* | E (4) |  | 0 | Doubles as Tx for TSP (D<=19); Doubles as Xwire data (D>=20) for boards labelled MMi203 (c.f. MMi203A)
5 | 16 | 17* | F (5) |  | 1 | Doubles as Rx for TSP (D<=19)
6 | 17 | 18* | G (6) |  | 2 |
7 | 13* | 19 | H (7) |  | 3 | Doubles as option link
8 | 14* | 20 |  | J (9) | 4 | Doubles as option link
9 | 18 | 21 |  | I (8)* | 5 | Doubles as the programmable thermistor drive voltage.
10,11,12 | 0V (system ground)

The U(n) column refers to the position in U of the pin's configuration byte during a SPiceConfigU instruction.

Analog channel designators are show both in the old letter style and the newer (dialect 18 floating point analog I/O) numeric channel numbers.

The analog output function (pins 8 and 9) is actually a pulse width modulated (PWM) digital output. On a SPice board this can be filtered to yield a smooth DC voltage, giving an analog output. The PWM signal on SPice pin 9 is also used for the thermistor drive voltage on the main board; naturally it can only be used for one thing at a time.

Pins 4 through 9 can all be set up for different functions with different SPice boards. The function of the pins is set in your program using the SPiceConfigU instruction. Default settings are marked *.

Note that SPice pins 7 and 8 use the same processor pins as the option links. Using them for SPice precludes using them as option links, and visa versa. If one or both of those two pins is used on a SPice board then you must remove the corresponding option jumper.

The 5mA permissible load on the +5V supply pin is a nominal allowance that has been included in the overall power supply budget with board supply voltages up to the maximum rating of 32V. This can be increased significantly if lower supply voltages are used.

We do not intend users to use the SPice connector other than for add-on boards supplied by us. The above information is provided mainly to make you aware of the possibilities, perhaps to inspire you to have a custom SPice board made.

Note: TSP has been discontinued


MMi203: Xwire

The MMi203 with Firmware dialect 20 and later supports Xwire. Xwire is an easy to use method of connecting several boards together into a multiprocessor network.

Boards labelled as MMi203A

There are two 2-pin connectors for Xwire near the power connector.

Boards labelled MMi203 (older versions)

The Xwire connections are on the SPice connector.

The board's Xwire address is set via your program, if it is to be a slave.

Xwire documentation.

The maximum number of data bytes that can be transferred into or out of the MMi203 via Xwire is 32.

Electrically the MMi203 can drive 10 unit loads and represents one unit load. That means one MMi203 board can drive 10 other MMi203s or other "unit load" boards.

The master configuration table may contain a maximum of 50 entries. This is more than the hardware limit. Conceivably one slave board might have more than one logical device (address) on it.


MMi202: Comms connector

This does not apply to MMi203

The comms connector is a standard DB9. It provides an RS232 compatible interface. It is used for communications with a PC for programming and use with SPLatLink.

The RS232 interface is a basic interface with transmit data, receive data and ground. Pinouts are as follows:

Pin | Signal name | Comments
1 |  | Not connected
2 | RxD | Serial data output from board
3 | TxD | Serial data input to board
4 |  | Not connected
5 | Gnd | 0V, signal ground
6 | DSR | Data Set Ready, switchable output line.
7 |  | Not connected
8 | CTS | Hard-wired to +5V for accessory power
9 |  | Not connected

This is a DCE ("looks like a modem") interface, suitable for connection to a DTE, such as a computer, with a straight-through cable.

To connect the SPLat to another DCE, like a modem, you must use a crossover cable. The cable will need the following wiring:

SPLat pin | Modem pin
1 |
2 RxD | 3 TxD
3 TxD | 2 RxD
4 |
5 Gnd | 5 Gnd
6 DSR | 7 RTS
7 |
8 CTS | 4 DTR
9 |

Built in multi-drop capability

The interface has a special design that allows several boards' interfaces to be connected in parallel, like pin to like pin. This makes possible small systems of up to 5 boards in a multi-drop communications configuration, connected perhaps to a shared modem. Please contact SPLat Controls for information about suitable Firmware to support this arrangement.

See also RS232 pinouts: A primer


MMi203: Comms connector

The comms connector is a standard DB9. It provides an RS232 compatible interface. It is used for communications with a PC for programming and use with SPLatLink.

The RS232 interface is a basic interface with transmit data, receive data and ground. Pinouts are as follows:

Pin | Signal name | Comments
1 |  | Not connected
2 | RxD | Serial data output from board
3 | TxD | Serial data input to board
4 |  | Not connected
5 | Gnd | 0V, signal ground
6 |  | Not connected
7 |  | Not connected
8 | CTS | Hard-wired to +5V for accessory power
9 |  | Not connected

This is a DCE ("looks like a modem") interface, suitable for connection to a DTE, such as a computer, with a straight-through cable.

To connect the SPLat to another DCE, like a modem, you must use a crossover cable. The cable will need the following wiring:

SPLat pin | Modem pin
1 |
2 RxD | 3 TxD
3 TxD | 2 RxD
4 |
5 Gnd | 5 Gnd
6 |
7 |
8 CTS | 4 DTR
9 |

The SPLat cannot controls the RTS pin of a modem.

Built in multi-drop capability

The interface has a special design that allows several boards' interfaces to be connected in parallel, like pin to like pin. This makes possible small systems of up to 5 boards in a multi-drop communications configuration, connected perhaps to a shared modem. Please contact SPLat Controls for information about suitable Firmware to support this arrangement.

See also RS232 pinouts: A primer


MMi203: Communications

The MMi203 implements 3 serial communications protocols: Our proprietary SPLatLink, the industry standard ModBus RTU (master or slave), and the "User programmable protocol".

These protocols use the programming (serial) port. This is an RS232 compatible port with a 9-pin connector. This will connect directly to any PC that has an RS232 (COM port), or to any other device that has RS232, given the right cable. The maximum cable length is 5m. You can also use an MB485 adaptor to convert it to RS485, which will allow connections up to hundreds of meters.

User programmable protocol

This refers to the ability of SPLat to be programmed to send and received arbitrary characters. More information.

ModBus RTU Slave

ModBus is the oldest and most widely accepted protocol in automation. This controller supports ModBus RTU slave mode. In the initial implementations the ModBus feature allows message lengths up to the ModBus limit of 255 bytes. This may be reduced in the future as we add new features and have to "take back" some of the memory. In practice a 64-byte message can transfer 57 bytes of data.

More information on ModBus.

ModBus RTU Master

This controller supports ModBus RTU Master mode.

SPLatLink

SPLatLink is our proprietary protocol for communicating with a SPLat controller while it is running your SPLat Program.

SPLatLink is deprecated. We discourage its use in custom applications.


MMi203: MultiTrack

Controllers with dialect 16 or greater contain the MultiTrack multitasking system. The capacity is 32 concurrent tasks.


MMi203: Operating temperature ratings

The maximum allowable operating temperature of the board is governed by several factors.To determine the highest permissible operating temperature you need to evaluate each factor separately and then use the worst (lowest temperature) answer.

The factors that must be taken into account are:

  1. The semiconductors on the board have a maximum ambient temperature rating of 70°C.
  2. The LCD, if you are using one, may suffer from reduced readability at elevated temperatures. If you are supplying your own, make sure to select one with a suitable temperature rating. The ones we supply are rated to 70°C but may suffer a reversible loss of readability over 60°C.
  3. The 5V power supply regulator will shut down and self-protect if its junction temperature exceeds 150°C. How hot it actually gets depends heavily on the supply voltage. The table below will help you with this factor.
  4. The chip driving the outputs will also self-heat, and eventually fail if run too hot for too long. Evaluating this factor is discussed under MMi201 External digital outputs
  5. Hot components fail faster than cool components. For semiconductors, lowering the temperature by 10°C doubles the life time.

5V regulator

Allowing 3mA for an LCD and 5mA for a SPice board, the worst case load on the LM317 regulator is 64mA. With a maximum permissible operating junction temperature of 125°C and a junction to ambient thermal resistance of 50°C/W, this translates into the following maximum ambient temperatures versus supply voltage:

Supply V | Temperature
up to 22V | 70°C
24V | 64°C
28V | 50°C
32V | 35°C

Note: Figures at 28V and 32V are given for reference only. We do not recommend operating the board at voltages over 24V for sustained periods of time. The board can be damaged if the power supply voltage goes over 32V, even momentarily.


MMi203: Firmware revisions and reFlashing

This chapter contains the two related topics of Firmware revisions/updates and reFlashing.

  1. reFlash is the ability of a SPLat board to have new operating firmware loaded into it. The operating firmware is the program that we load into the board, that gives it its SPLat "personality". This is quite different from the program you write to tailor it to your application, which we call SPLatWare.
  2. reFlash is available in 2nd generation boards, starting with the MMi99 with firmware version 3.3 or later (reFlash was developed after the earlier MMi99 versions were released). Older designs, which do not have reFlash, are SP108, SL88, MMi88, AJ18, PMC3, CA20, OEM32 and OEM36.
  3. There are three reasons why you would ever want to reFlash a SPLat board:
  4. Because we introduce new firmware features quite frequently, it has become impossible to ensure that all our (and our dealers') inventory is fully up to date. Therefore it is possible that a board you receive does not contain the "latest and greatest". Thanks to the miracle of reFlash technology you can still update your board to the latest v

All MMi20x boards are reFlashable. Only MMi99 with firmware version 3.3 or later are reFlashable (earlier versions are not). reFlashing is discussed more thoroughly elsewhere in the SKB.

We goofed!

We released the MMi203 with the same internal board I.D. code as the MMi202. This means that in SPLat/PC it will appear as MMi202, which is not particularly serious. However, it also means it is possible to reFlash the wrong firmware into both board types. Please very very careful when reflashing not to "cross-reFlash"! (MMi202 firmware in an MMi203 is easily recoverable, if you have a MMi203 reFlash file. MMi203 firmware in an MMi202 is also recoverable, but trickier).

Previous Topic

Next Topic

In This Chapter

MMi202: Firmware version history and updates

MMi201: Firmware version history and updates

MMi200: Firmware version history and updates

MMi99: Firmware version history and updates


MMi202: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for MMi202. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

Caution: Be very sure not to reFlash any of these into an MMi203. We goofed on the MMi203, so it will except an MMi202 Firmware, but it will not work with incorrect firmware.

Version / date

rfl file

Description

V3.34

08Mar11

MMi202v3-34.rfl

  • Dialect 26 (ComRx_fGetNum n)

V3.29

23Aug10

MMi202v3-29.rfl

  • Various enhancements to Xwire

V3.28

11Aug10

MMi202v3-28.rfl

  • Dialect 23 support
  • Various enhancements to Xwire
  • Bug: implemented jndexing of FixToMem16S, FixToMem16U, FloatMem16U, and FloatMem16S
  • ModBus made more robust when working with 3rd party devices that are non-compliant with the ModBus 3.5 character time end of message delay.

V3.24

20Jan10

MMi202v3-24.rfl

  • Dialect 21 support
  • Fast program download support. Works with SPLat/PC 10.21.1 and later
  • OBLCD instructions can be mapped to an external HMI via ModBus
  • CPU temperature readout

V3.21

25Mar09

MMi202v3-21.rfl

  • Added ModBus Master RTU mode
  • Support for dimmable LCD backlight
  • Improved accuracy of analog outputs
  • Atomic handling of multi-byte variables in Xwire receive
  • Bug fix in boot loader
  • Extended LCD custom characters to allow large format 7-segment digit generation

V3.20

17Jun08

MMi202v3-20.rfl

  • Fixed Xwire bug

V3.16

8Oct07

MMi202v3-16.rfl

  • Bug: Reporting wrong version number

V3.15

29Aug07

MMi202v3-15.rfl

  • Dialect 20, adds Xwire, 10mS timebase adjustment
  • Discontinues TSP

V3.13 23Mar07

MMi202v3-13.rfl

  • Dialect 19, adds OBCB
  • Bug fixes

V3.0

N/A

Initial release. Dialect 18


MMi201: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for MMi201. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date

rfl file

Description

V3.17

9Jan07

mmi201v3-17.rfl

Bug fixes related to a bug in the processor affecting the RTC. This is the last MMi201 release.

V3.15

5Sep06

mmi201v3-15.rfl

Dialect 18 upgrade: fAnIn, fAnOut, ClrInstCount, OBLCD_HGraph

V3.14

4Jun06

mmi201v3-14.rfl

Dialect 17: Partial ModBus implementation. Limitations are noted in ModBus documentation.

V3.12

3Feb06

mmi201v3-12.rfl

Resolves a problem arising from a bug in the RTC chip

V3.11

10Jan06

mmi201v3-11.rfl

Dialect 16 upgrade, adding over 170 new instructions and a raft of new features, including:

  • MultiTrack
  • Real Time Clock support
  • SuperTimers (10mS to 46 hours)
  • Quadrature counting
  • NVEM page 0
  • Enhanced LCD buffering

V3.1

N/A

Initial release.


MMi200: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for MMi200. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

reFlash files are not interchangeable across different board models. This page is for the MMi200.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date | rfl file | Description
V3.12; 01Feb05 | mmi200v3-12.rfl | Fixed incorrect operation of OBCA above 1024 counts. This is the last MMi200 release.
V3.0 |  | Original release


MMi99: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for MMi99. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

reFlash files are not interchangeable across different board models. This page is for the MMi99.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date | rfl file | Description
V3.23; 18Apr06 | mmi99v3-23.rfl | Dialect 14 support, including TSP. This will be the last update for MMi99.
V3.22; 10May04 | mmi99v3-22.rfl | Added Mode 4 (totalizing counter) to the OBCA; Fixed incorrect handling of indexing in fStore, fRecallW and fRecallQ
V3.20 05Feb04 | mmi99v3-20.rfl | Fixed bug in OutputM introduced in 3.18
V3.19; 28Oct03 | mmi99v3-19.rfl | Fixed bug in CPU device. NOTE: You will have to install the V3.0 reFlash utility to use with this and later upgrades. The older V2.x will not work.
V3.18 10Oct03 | mmi99v3-18.rfl | Added dialect 12 instructions: Semaphores, GetTick10, GetTick100, Suspend, Resume; Increased RAM to 192 bytes and subroutine stack to 6 levels. Extended scope of InputFM and OutputM; Implemented CPU device attributes: Firmware revision; Dialect; Board type; Subroutine nesting depth; Processor reset flags
V3.15 12May03 | mmi99v3-15.rfl | Fixed bug that could lead to undesirable interactions between onboard UV peripherals (notably OBCA interfering with OBLCD); Added capability of driving large numbers of XPice boards in SPiceX mode.
V3.13 13Mar03 | mmi99v3-13.rfl | Corrected bug that causes front panel LEDs to run the wrong way (top=9 to bottom=15) in board revisions date coded 0207 (February 2002) or later.
V3.12 28Jan03 | mmi99v3-12.rfl | Corrected default for the InputK beep function from off to on. See KBeepOn, KBeepOff
V3.11; 25Nov02 | mmi99v3-11.rfl | Added dialect 11 instructions: DecMRetIfZ, DecMRetIfNZ; KBeepOn, KBeepOff
V3.10; 24Oct02 | mmi99v3-10.rfl | Added OnBoard Counter type A (OBCA); Fixed bug restricting U to 19 byte transfers; Note: Please use reFlasher utility V2.5 or later to ensure the revision number is displayed as 3.10 and not 3.A
V3.9 |  | Not released as rfl file
V3.8; 15Jul02 | mmi99V3-8.rfl | Fixed several minor bugs in UV transfer instructions; Fixed incorrect handling of invalid argument in RetIfMZ and RetIfMNZ
V3.7; 11Jul02 | mmi99V3-7.rfl | Added support for XPice expansion boards. Fixes problem in accessing analog outputs via SPLatLink
V3.6 | mmi99V3.6.rfl | Fixed bug whereby byte 19 of U could not be used. The practical effect of this is that LCDs with 20 character lines would not work properly.
V3.5 6Apr02 | mmi99V3-5.rfl | First version of rfl file released: Fixes problem in accessing analog inputs via SPLatLink


HD8 Product documentation

Budget priced readout controller

Congratulations on your choice of the SPLat HD8 controller. SPLat gives you the ability to implement a sophisticated control system in your product easily and inexpensively, and will give you an edge that will have your competitors wondering whatever hit them!

The SPLat HD8 controller gives you a feature set rivalling competing products costing many times as much. Although costing about the same as "programmable relays", entry level single board PLCs and hobbyist products, it offers considerably more capabilities. The HD8 has been designed for applications requiring a small but sophisticated controller with a basic readout, at an absolute minimal cost.

  • 3 bright, easy to read 12.5mm LED readout digits;
  • 3 operator push buttons, fully programmable functionality;
  • Real-world I/O: 850mA outputs and ruggedized inputs;
  • 2 analog inputs 0-5V, 0-10V, 0-20mA or temperature;
  • Easier to program than C, Basic or ladder;
  • Large program memory (over 20K bytes, approx 12,000 instructions);
  • Capable of sophisticated control functions including math, state machines, PID, algorithms, heuristics and lookup tables;
  • Built in multitasking;
  • Able to be networked with other Xwire compatible SPLat controllers and peripherals
  • Fast: Executes approximately 15,000 high level SPLat instructions per second;
  • Expansion connector for all SPLat expansion boards.

Have your first working program in less than an hour!

Like all SPLat controllers, the HD8 is extremely easy to get started with. Our programming software is free, and includes a full simulator that allows you to test your program and explore possibilities without any hardware present. Our programming language has been skillfully crafted to make the simple tasks absurdly easy to program, and yet there is a rich repertoire of advanced instructions to satisfy the sophisticated user. Our training materials are legendary (and free). The HD8 contains real world input/output circuits, so you won't have to design your own.

Every minute you invest in SPLat is an investment that lasts!

Best of all, the HD8 programming is fully compatible with our other controllers. That means if you move up to say an MMi202, or even go for a custom SPLat controller one day, everything you have learnt and 98% of the code you have written will transfer directly. SPLat is the only control system you can get started with for the price of a night out and then stay with until you are using thousands of custom units.

How do we do it?

You may be sceptical about how we can deliver so much performance at such a budget price. When we design a controller we aim for the best possible value for money. We design and re-design our circuitry to eliminate unnecessary or expensive components. As far as possible we select multi-vendor components so we can shop around. Maybe most importantly, we are engineers designing for end users, not engineers designing for engineers. We don't allow ourselves to be carried away by megahertz and gigabytes. We understand that what is really important to an OEM operating in the real world is getting the necessary performance at a price that will give a competitive advantage.

One thing we are obsessive about is build quality. We hate returns, and get very few of them (way less than 1%). Every board we make is built with strict process controls. Every board is visually inspected. Every board is 100% functionally tested. And every board is backed by our 2-year standard product guarantee and Post-Nuke repair policy.

Previous Topic

Next Topic

In This Chapter

HD8: Introduction

HD8: Connections

HD8: Power supply

HD8: External inputs and outputs

HD8: Operator interface

HD8: Non-volatile memory

HD8: OnBoard Counter type B

HD8: OnBoard Quadrature Counter

HD8: CPU device

HD8: Option links/Xwire address setting

HD8: Comms connector

HD8: Communications

HD8: Xwire

HD8: Programming

HD8: Operating temperature ratings

HD8: Firmware revisions and reFlashing


HD8: Introduction

This document provides you with detailed technical information on the SPLat HD8 programmable controller.

This document is part of the SPLat Knowledge Base (SKB). The SKB is a comprehensive collection of documents covering all technical aspects of the SPLat product. It is published in electronic formats (HTML-Help and HTML) and is also available electronically as a series of document files you can print out yourself. By publishing the documents only in electronic form, we not only save some trees but are also able to make updates available to you very quickly on our website. We suggest you check our Website periodically to ensure you have the most recent version.

Previous Topic

Next Topic

In This Chapter

HD8: Using this document

Other resources

Getting started with your controller

HD8: Product overview


HD8: Using this document

This document concerns itself primarily with information that is specific to the HD8 product. In particular, it concentrates on the hardware aspects of the HD8. Because programming is uniform across all SPLat products, detailed SPLat programming information is located elsewhere. For more introductory information on programming please see HD8 Programming within this document. That section contains some HD8-specific information, such as programming the 7-segment numeric display.

The material is organized largely according to various hardware features. This should allow you to quickly locate the information you need in order to properly match the HD8 to the outside world and make electrical connections. There are also a couple of chapters on more general topics like determining the permissible operating temperature.

We suggest you start with a quick read through the whole document to familiarize yourself with what's there. After that you should be able to use it as a technical reference to look up specific information you need.

Most technical product manuals contain a Specifications section. We've done it a bit differently (we do many things differently, which is probably why you are using SPLat!). The numbers and ratings are discussed as part of each feature. While this may seem to make the information a little harder to locate (compared to a consolidated table), it gives us the ability to loosen up a little and discuss how the operating envelope can be flexed under different conditions. For example, the allowable output current depends on the operating ambient temperature.


HD8: Product overview

The HD8 is a SPLat single board programmable logic controller that includes a number of innovative features. Key features are:

  • 4 bidirectional digital I/O points. Each can be used as an input or as an output.
  • 3 seven-segment red LED numeric display digits.
  • 3 push button switches;
  • 2 analog inputs with selectable ranges: 0-5V, 0-10V, 0-20mA or thermistor.
  • 2 TTL/CMOS (5V) level pulse width modulated (PWM) outputs suitable for driving RC servos.
  • Xwire serial expansion bus to allow multiple controllers to be networked and/or connection of a range of special function peripheral boards.
  • Xpice expansion connector to allow use of a range of standard I/O expanders.

The most unusual feature of the SPLat HD8 is its built in operator interface. This gives you push buttons and a 3-digit numeric display for operator interaction.

The HD8 is supplied two different packages.

  • HD8DK. This is the Developer's Kit. It consists of the HD8 board plus programming software, programming cable and a set of matching connectors;
  • HD8OEM. This is the "production" version of the board (only) with matching connectors.


HD8: Connections

The above picture shows all the connectors used for making external connections to the HD8 These are:

Bidirectional digital I/O

This is a 6-pin connector for the 4 digital I/O points, which can each be used as an input or an output. The connector has 4 signal pins as well as connection to the positive supply voltage and to common (ground). The wiring for these is detailed separately for input mode and output mode.

Analog inputs

These two 2-pin connectors are the analog inputs.

Analog outputs

A 4-pin connector for two analog outputs.

Power

This 3-pin connector is for applying power to the HD8. The wiring is described under HD8 Power supply.

Comms

This is the connector you connect to for programming the HD8. This is NOT an RS232 connector. A PC232 programming cable adaptor is required for connecting to a PC for programming.

Expansion

This connector is for expansion boards like the XBIO16. The SPx expansion scheme is documented elsewhere in the SPLat Knowledge Base.

Xwire

Xwire bus connections. The two connectors are identical and wired in parallel. The lefthand pin as shown is 0V (ground). The righthand pin is bidirectional serial data.

Analog inputs

These two 2-pin connectors are the analog inputs.

Analog outputs

A 4-pin connector for two analog outputs.

Previous Topic

Next Topic

In This Chapter

HD8: Where to get matching connectors

Some common terminology


HD8: Where to get matching connectors

All connections to the HD8 are via 0.1" (2.54mm) pitch single inline friction lock connectors. These are frequently referred to as "KK" type, which is the name given to them by Molex. All sale versions versions come with matching connectors, but you may want to source them in a different form for mass production manufacture of wiring looms.

You would normally buy the connectors as plastic housings plus (separately) crimp terminals. The Molex part numbers are 22-01-2xx5 for the housings (xx=number of ways) and 08-50-0032 for the terminals. You can buy these from Farnell, Mouser, Digikey and many other suppliers.

The connector sizes you need for the HD8 are:

Digital I/O: 6-way

Analog in: 2-way

PWM out: 4-way

Xwire: 2-way

Power: 3-way


HD8: Power supply

The above illustration shows the power connections to the HD8. We have used a 3-pin connector, to differentiate from 2-pin connectors used elsewhere on the board. There are two pins for 0V (gnd, common). You do not have to wire both to both 0V pins; either one will do. The wire shown in red above should go to the positive terminal of your power supply. The black wire should go to the ground or common terminal of the power supply (often labelled minus on a single voltage supply). See also 'Some common terminology':

Supply voltage

In general, the HD8 needs a supply voltage between 10VDC and 24VDC. The voltage is allowed to go to a maximum of 32V. Note that the HD8 operating temperature ratings depend on the supply voltage.

It's quite OK to use an unregulated power supply. Just remember that the ripple will make the voltage instantaneously less than the average DC value, and the board will respond to the instantaneous value. Remember, too, that an unregulated supply can go quite a bit higher than its nominal value, especially when lightly loaded and if the mains voltage is high. In general we suggest you use a 24VDC switchmode power supply. These are now so inexpensive that there is little reason not to use one.

Supply current

The current drawn by the board will depend on the supply voltage and what the board is doing. The maximum supply current draw versus supply voltage is:

Supply voltage | Current
10V | 66mA
12V | 56mA
15V | 47mA
18V | 42mA
24V | 37mA
28V | (36mA)
32V | (36mA)

These figures are based on everything being on at once. The figures do not include any external loads such as relays. The HD8 uses an onboard switchmode voltage regulator, which is why the current actually decreases with increasing supply voltage.

Note: Figures at 28V and 32V are given for reference only. We do not recommend operating the board at voltages over 24V for sustained periods of time. The board can be damaged if the power supply voltage goes over 32V, even momentarily.


HD8: External inputs and outputs

The HD8 contains 4 digital I/O points which can be used as inputs or outputs. We call these "bidirectional". In effect they consist of an input and an output brought out to a single pin. If the output is OFF, an externally connected device (say an NPN sensor or a contact) can control the on/off state of the pin. If the output is turned on, it connects the I/O pin to 0V via a transistor switch and it registers an ON input. Hence to use a pin as in input you simply make sure its output circuit is turned off.

Previous Topic

Next Topic

In This Chapter

HD8: Bidirectional points used as inputs

HD8: Bidirectional points used as outputs

HD8: Analog inputs

HD8: Analog outputs


HD8: Bidirectional points used as inputs

The above picture shows the two of the four bidirectional digital I/O pins used with a couple of typical input devices. An input is deemed to be on when the I/O pin is connected to 0V through a contact or an NPN sensor.

The digital I/O connector contains the connections for 4 inputs, plus pins that connect onboard to the power supply positive and common/0V terminals. The common pin on each input connector is provided as a convenience to you when wiring inputs to the board.

To program the point as an input simply never turn on the corresponding output (it defaults to off at power up). If your program does turn on the output, then as an input it will read ON.

In the picture A is a simple switch connected to I/O point 0. B is the output of an NPN style sensor connected to input 3. The sensor would also require a positive supply voltage to power it. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

As an input the bidirectional pins are ON when the pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - V(supply) / 10000

The leading minus sign simply means the current is flowing out of the pin. The 10000 represents the 10K input pullup resistance.

This equation translates to 1.2mA with a 12V supply and 2.4mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognise the input as ON when the voltage on the input pin is typically less than 4.6V (3V worst case). That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input is approximately 4.6V or a little less. However, due to tolerances (variations) between batches of components an input is guaranteed to be treated as low (in our case ON) only if the voltage is below 3V. An input is guaranteed to be treated as high (OFF) only if the voltage is above 7V. The range between 3V and 7V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

Note that the above threshold voltages vary from product to product. Please check the specific documentation on the board(s) you are using.

How to blow up the bidirectional I/O points when used as inputs.

  1. Apply a voltage more negative than the 0V (common) terminal.
  2. Apply a voltage over +32V


HD8: Bidirectional points used as outputs

The adjacent picture illustrates three of the four bidirectional I/O points connected to some typical output loads. When an output is ON it connects the I/O pin to 0V via a Darlington transistor.

The connector is wired to the 4 bidirectional I/O pins. Each connector also has a "convenience" pin connected to power supply positive (as well as one for 0V, which is used for inputs. The output load should always be connected between the output pin and power supply positive. Use the +Supply convenience pin on the I/O connector in preference to wiring directly to the power supply +.

CAUTION: Take care never to short an output pin to the positive common pin. If the output is on at the time you will blow up the output chip instantly!

The loads illustrated are:

A: The input pins of a solid state relay. Note the polarity!

B: A relay coil. Notice we have included a catch diode across the coil. While there are diodes on the board we recommend a diode across the coil, as close as possible to the coil, to minimize problems with switching noise. A suitable diode is a 1N4001 or similar 1A/50V rectifier diode.

C: An LED, with a series current limiting resistor. Resistance calculators

The (digital) outputs are implemented using the ULN2803A, a chip that contains Darlington transistors. The outputs consist of transistor switches between the output pin and 0V. When the output transistor is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output chip will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built into the chip. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the relay coil as close as possible to the relay, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

Maximum allowable output current

The HD8 uses a ULN2803A chip. This contains 8 Darlington transistors. The transistors are connected in parallel pairs to provide 4 outputs. Each output pair is capable of handling more current than a single transistor, but not twice as much. This restriction is due to imperfect current sharing between 2 parallel transistors. The chip data sheet does not contain enough information to allow accurate prediction of how well the transistors will share the current, so the following ratings are based on what we believe to be a conservative estimate of the chip's capabilities.

The maximum allowable output current is 870mA steady state, with brief (100mS) peaks of 800mA allowed.

There is also a limit to how much total current one ULN2803A chip can handle. This relates to the internal heating of the chip. Let's look at 2 extreme cases:

  1. All 4 outputs conducting the same current
  2. The allowable current concentrated in a few outputs.

Equal currents

The following table lists allowable current versus ambient temperature, when all 4 outputs of the HD8 are conducting the same current at once:

Temperature | Allowable current per output
25°C | 365mA
40°C | 320mA
50°C | 290mA
60°C | 260mA
70°C | 225mA

Concentrated currents

The following table shows the maximum allowable currents, when concentrated in a few outputs, versus temperature

Temp | 1st O/P | 2nd O/P | 3rd O/P | 4th O/P
25°C | 870mA | 250mA | 0 | 0
40°C | 870mA | 0 | 0 | 0
50°C | 805mA | 0 | 0 | 0
60°C | 730mA | 0 | 0 | 0
70°C | 650mA | 0 | 0 | 0

Naturally there are many other possible combinations, but the above should serve as a guide.

The above figures are intended as a guide only. In any event, the absolute maximum ambient operating temperature for the ULN2803A chip is 70°C.

If you feel like doing the actual sums, here's the procedure.

  1. For each output, calculate its output voltage drop from the formula
    V(out) = 0..88 + 1 * I(out) [ I(out) in Amperes ]
  2. For each output calculate the dissipation in watts from the formula
    P=V(out) * I(out)
  3. Add up all the dissipation figures to give P(tot) in watts
  4. Calculate the maximum allowable ambient temperature from the formula
    Ta(max) = 125 - P(tot) * 55
  5. If you get an answer over 70°C then use 70°C

This calculation allows for the fact that the current is split between 2 transistors with imperfect current sharing.

This calculation aims at a maximum internal (junction) temperature of 125°C, which is the maximum rating of the chip. There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better.

Note: This discussion pertains to the maximum operating temperature for the ULN2803A output chip only. See HD8 Operating temperature ratings for a further discussion.

How to blow up the outputs

Here are some of the ways you can blow up the outputs:

  1. Connecting the output pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than the output current rating)
  2. Connecting the output pin to any negative voltage
  3. Connecting the output pin to any voltage greater than the positive supply.
  4. Switching a load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  5. Using an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.
  6. Driving an incandescent lamp. Incandescent lamps draw a warm-up surge current of many (up to 20) times their rated steady state current.

After you've blown up the outputs

We have thoughtfully fitted the ULN2803A output chip in a socket for you, so you can replace it without soldering. We can supply a 6-pack of ULN2803A chips. Our product code is ULNx6, and is available on our website. Alternatively you can buy the chip wherever fine chips are sold, including many electronics hobbyist stores.. Here are some supplier names and order codes:-

Supplier | Order code
Digikey (USA/International) | Search site
Mouser (USA/International) | 511-E-ULN2803A
Altronics (Australia) | Z3010
Element 14 (formerly Farnell Electronics) (UK, Europe, Australia, NZ) | 705-159, 409-868, 318-7032

The ULN2803A is made by many manufacturers, including Motorola, Texas Instruments, Alegro, Toshiba and ST Microelectronics. You should be able to buy them from most electronic component distributors and even from major electronic hobby stores.

When semiconductor switches fail they are more likely to become stuck ON than OFF.


HD8: Analog inputs

The picture above shows the connectors used for the two analog inputs, and the associated configuration jumpers.

There are two analog input channels on the HD8 board. They can be configured to operate on 0-5V, 0-10V, 0-20mA or as temperature measuring inputs (using an external thermistor sensor). In all cases the input uses one input pin and one common pin. The common pins on the analog connectors are tied to the board's 0V terminal. Please see the article on common terminology for an explanation of the 0V terminal.

The analog inputs are designated as analog A and B in AnIn instructions or 0 and 1 in fAnIn instructions. It has a 4-pin configuration jumper field. A single jumper (shunt) on a pair of jumper pins selects the operating mode (V=voltage, I=current or T=temperature). The picture shows them both in temperature mode.

Using voltage mode

To use an analog input in voltage mode you can either

  • set its jumper to bridge the two pins either side of the letter V for 0-10V range or
  • remove the jumper entirely for 0-5V range,

and apply the input voltage between the input (positive) and the common (most negative). When the AnInA or AnInB instruction is executed you will get a number between 0 and 255 in the X-register (8 bit resolution). 0 represents 0 input and 255 represents full scale.

You can also use the fAnIn instruction. This will return a normalized floating point number in W which ranges from 0 at 0V in to 1.000 at full scale in. The range 0 to 1.0 is divided into 1023 steps of 0.00097752 (10-bit resolution). fAnIn selects the required channel with a numeric argument, 0 for input A, 1 for input B. The channel number in fAnIn is jndexed.

In 0-10V range the input resistance is 200KOhm. In the 0-5V range it is several mega-ohms. The accuracy is about 1.5% of full scale. There is an input noise filter comprising a single RC time constant of 5mS (0-10V) or 10mS (0-5V).

Using current mode

To use the analog input in current mode you set its jumper to bridge the two pins either side of the letter I. You then feed the current to be measured into the input pin, with the return out of the common pin. The current source (e.g. 4-20mA transmitter) must be of the type that sources a positive current from its output pin and sinks (returns) the current into its ground (or minus out) pin. Most 4-20mA transmitters are "line powered" and have a simple 2-wire connection. Connect the positive pin of the transmitter to the positive supply voltage and the negative pin to the SPLat input pin.

Some 4-20mA devices are non-loop powered. These have 3 connection wires: Positive supply, ground/negative supply and output. To connect such a device to the SPLat provide it power to its positive and ground pins, connect its ground pin to the SPLat 0V and its output to the SPLat analog input.

When the AnInA/AnInB instruction is executed, you will get a number between 0 and 255 in the X-register. 0 represents 0mA and 255 represents 20mA. At 4mA you will get a reading of 51 (plus or minus any conversion error). You can also use fAnIN, which will return 0 for 0mA, 1.0 for 20mA and 0.2 for 4mA (plus or minus any conversion error).

In current mode the input resistance is 250Ohms. The accuracy is about 1.5% of full scale. There is an input noise filter comprising a single RC time constant of 10mS.

Using thermistor mode

To use the analog input in temperature mode you set its jumper to bridge the two pins either side of the letter T and connect a suitable thermistor between the analog input pin and common. There are two basic types of thermistor: Positive Temperature Coefficient (PTC) and Negative Temperature Coefficient (NTC). The latter, NTC, is the type used for temperature measurement with SPLat. Typically, an NTC thermistor is specified as having a certain resistance at 25 degrees C.

The adjacent diagram shows the thermistor measuring circuit.

The thermistor is the bottom resistor in a voltage divider. The top resistor is a fixed onboard 10.25KOhm resistor. The voltage divider is driven by a fixed 5V source that can be switched on and off via output 4. This is an "onboard output" that is used for thermistor drive only. The purpose of switching it on and off is to minimize self-heating of the thermistor.

This arrangement differs from other models of SPLat products, where the thermistor drive voltage is programmable.

In thermistor mode the full scale analog input voltage is 5V.

A thermistor is an electronic component that changes resistance with temperature. Don't confuse this with an RTD sensor, which also changes resistance with temperature but is much less sensitive! The warmer an NTC thermistor gets, the lower its resistance. Hence, a reading of the input voltage can be used to calculate the thermistor temperature.

You will notice that there is a 10mS time constant in the measuring circuit. This is provided to filter out hum and other noise and reduce its impact on the reading accuracy. At the same time, the 5V drive voltage is switchable via output 4, to help reduce self-heating of the thermistor. Because of the time constant, when you switch on the drive voltage it will take some time for the reading to settle down. This means your program should turn on the output, wait long enough for the reading to settle, and only then take a reading. How long should this settling time be? About 7 time constants (70mS) to settle to 0.1% (which is 10-bit resolution).

So how much error will you get from self-heating of the thermistor (assuming you don't switch the drive)? Maximum heat dissipation (and hence self-heating) will occur when the thermistor resistance equals the drive resistance, or about 10K. The dissipation will be 0.625mW. Typically a small thermistor will self-heat by about 1°C per mW in still, free air. Hence the self heating would be about 0.6°C. In moving air, or with an enlarged surface area (say attached to a fin), or in liquid, the figure would be much less.

Accuracy and resolution depend on the thermistor you use and how you program it. The best achievable figures are about 0.1°C for both, if the system includes provision for software calibration. The SPLat Knowledge Base contains a tutorial on thermistor temperature measurement using SPLat boards. As a first approximation, when shopping for a thermistor for your application, shoot for one with a resistance of 10KOhm in the middle of the temperature range you need to cover.

How to blow up the analog input

Here are some ways of blowing up the analog inputs:

  1. In any mode, connect the input to a voltage greater than the positive supply or negative with respect to the 0V terminal.
  2. In current mode, apply more than 7.5V to the input. This will overheat the current shunt resistor and lead to loss of accuracy or, in the extreme, damage the board.


HD8: Analog outputs

The above picture shows the connections for the two external analog outputs. They are designated analog outputs C and D. Each output can generate a 0-10V signal, with a maximum load current of 5mA, corresponding to a 2KOhm load. The outputs have internal current limiting (which is not the same as being short circuit proof!).

The analog output voltage is a positive voltage measured with respect to the 0V terminal. It is programmed using the AnOutA or AnOutB instruction. With X=0 this will result in 0V out, with X=255 it will result in 10V out.

The output resolution is 8 bits (256 steps between 0 and full scale) when you use the AnOut# instructions and 10 bits when you use fAnOut. The output is generated as a filtered pulse width modulated (PWM) signal. The filter consists of a simple 100mS RC low pass filter. For a full scale step output change, say from 0V to 10V, this will take 230mS to settle to within 10% of the full change, 450mS to settle to 1% and 550mS to settle to 0.4%. (Rule of thumb: 0.7 time-constants per bit of resolution)

How to blow up the analog outputs

Here are some ways of blowing up the analog outputs:

  1. Connect the output to any positive voltage over 10V and program a 0V output.
  2. Short the output to 0V for extended periods of time (it may survive)
  3. Connect the output to a negative voltage, even momentarily.


HD8: Operator interface

The HD8 has a built in operator interface (Man-Machine interface). The operator interface consists of:

3 push buttons

These are miniature momentary action push buttons, wired to dedicated inputs. They are sensed by a program just like any other input, though the SPLat programming language contains special instructions that are designed to simplify programming user interface operations. The buttons appear to your program on inputs 4, 5, and 6.

Numeric Display

The 3 digit numeric display uses 7-segment LED technology. Numerals are formed by selectively turning on the 7 segments. Each digit is driven by 8 dedicated outputs, the eighth being a decimal point that is located to the right of its associated digit. Thus the number range that can be displayed is 0.01 to 999.

In 7-segment displays the convention is to use letters to designate the segments, according to the following diagram:

The following table defines the output numbers used for the segments in each of the 3 display digits.

 | Left | Middle | Right
a | 5 | 13 | 21
b | 6 | 14 | 22
c | 7 | 15 | 23
d | 8 | 16 | 24
e | 9 | 17 | 25
f | 10 | 18 | 26
g | 11 | 19 | 27
dp | 12 | 20 | 28

Faceplate and graphic overlay (you supply)

The HD8 is designed to be mounted behind a flat faceplate. The plate requires cutouts to match the buttons and digits. You will find dimensioned drawings in the files resources area of the resources CD or on our file resources FTP site. The board needs to be spaced off the faceplate so the tops of the push button switch stems fall minutely (<0.1mm) below the outside surface of the faceplate. You then place a flexible polyester or Lexan graphics overlay on the outside surface and the buttons can be activated by pressing them through the overlay. You can see more detail in the MMi202 documentation.

Warning: The board and the metal faceplate (or any other metallic surface that the board is mounted to) must be attached using metal screws and no insulating hardware so they are at the same potential. The mounting holes on the board are deliberately made with exposed metal. Failure to "bond" the face plate to the board will expose the board and the LCD to the risk of damage from static discharge.


HD8: Non-volatile memory

The term non-volatile memory means a form of memory that does not lose its data when the power goes off. It can be implemented using a variety of technologies. Your hard disk drive is non-volatile. The magnetic core memory used in computers in the 60's and 70's was non-volatile. Some systems use a small battery to keep the memory powered up even when main power is off. The technologies commonly used in SPLat boards are known as "EEPROM" and "FLASH" memory.

The controller implements three kinds of non-volatile memory: "Permanent" memory, "Shadow" memory and "Non-Volatile Extended Memory" (NVEM). The Permanent memory has existed in SPLat boards almost from the very beginning. Shadow memory is a much newer development made possible by advances in microcontroller technology. NVEM is even more recent, and was introduced into off the shelf products in dialect 16.

Permanent memory

This type of non-volatile memory was introduced into SPLat in March 1998. It allows just 13 bytes of RAM (data memory) to be saved into a non-volatile memory as a indivisible (all or nothing) block to be later restored. The two instructions for this are PermStore and PermRecall.

Shadow memory

This is newer type of non-volatile memory, introduced in March 2002 in language dialect 10. It allows all of RAM to be selectively backed up to a "shadow" memory in blocks of any size, and likewise to be restored in blocks of any size. It also allows the shadow memory to be cleared in blocks, and supports indexed and jndexed addressing for more sophisticated data management in larger projects.

The term shadow memory comes about because it is a region of non-volatile memory that overlaps the RAM. When a certain RAM location is written to the shadow memory, it is written to the same byte address within the shadow memory as its own RAM address.

The instructions for shadow memory are ShadowWrite, ShadowRead and ShadowClear. All these can be used as indexed as well as non-indexed instructions.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

NVEM page 0

The controller implements "NVEM page 0", which is a non-volatile memory that makes available any of the User Flash memory in the processor chip that is not used by your program.

Comparison between nonvolatile memory types

The following table compares the three nonvolatile memory types as implemented in most SPLat controllers. Actual figures vary between specific products. Make sure you read the descriptions of each type for your product.

Type | Size | Typical use | Endurance (in typical product) | Notes
Perm-mem | 13 bytes | Cycle counters, hour meters | 500K to 1M | Built in error detection. Can only be written and read as a single block. Does NOT survive downloading of a new program
Shadow | Same size as RAM | User configuration data | 10,000 total (not per byte!) | Very easy to use, works very well in menu systems. Can be read and written one byte at a time. Survives downloading of a new program
NVEM page 0 | However much of User Flash is not used by your program. Maximum 32K. | Lookup tables, infrequently changed configuration or pattern data, scripts | 10,000 per block. | Read often/write very infrequently. Writing is constrained by 512 (typical) byte write block size. Must be presumed corrupted if a new program is downloaded.

Endurance of non-volatile memories

Certain non-volatile memory technologies have a finite life, as defined by the number of writes or erases (clear cycles) they can endure. For permanent memory this translates to the total number of times a PermStore can be executed before the memory is likely to give erroneous data. For shadow memory it means the total number of ShadowWrite or ShadowClear instructions before it is likely to give errors. For NVEM0 it is the total of writes to any block of (typically) 512 bytes.

(In older products shadow memory was genuine byte-writable EEPROM, and the endurance was per byte. More recent models use main FLASH because the newer chips lack EEPROM, and each write entails re-writing the whole shadow block. Hence the endurance is now total writes to the block).

You should take great care to not write a program that will write or clear the non-volatile memory so often that it gets worn out too soon.

Example: Say a given non-volatile memory has a stated endurance of 100,000 cycles, and your program counts cans of sardines at a rate of 100 per minute, updating the non-volatile memory after every count. Your board would only last 1000 minutes, or about 2 shifts. If you re-write your program so it only updates the non-volatile memory once per hour, you would get 100,000 hours or about 12 years life, with some loss of accuracy as the trade-off.

CAUTION: You must take into account the possibility of power failing right in the middle of writing to the non-volatile memory. Unless specifically stated for the SPLat product you are using, there is always a possibility that data will be corrupted if that happen. You must therefore ensure, by the design of your system, that there will be no disastrous consequences of corrupted data.

The Permanent Memory has the best protection in this regard. Data is written with a checksum, and the PermRecall instruction provides a result indicator in the R register. This means that in the very least your program can have a good probability of avoiding the use of suspect data.

In the case of writing to NVEM page 0, the risk can be even worse. NVEM page zero follows directly on from the program memory. That means you could be writing to the same (typically 512 byte) block as the end of your program or read-only data tables. A power failure during a write would result in your program getting corrupted. You should therefore have an unused areas of 512 bytes between the program or read-only tables and the part of NVEM page 0 that is to be written to.

Caution: Exceeding the endurance of FLASH memory may damage the controller. It will also void the product warranty.

Support in SPLat/PC

Both types of non-volatile memory are supported in SPLat/PC.

The support for permanent memory is documented in the SPLat/PC "classic" help file. In the help file index search for "Permanent memory".

SPLat/PC simulates shadow memory by saving the shadow data in the computer's memory. The data is initialized to all zero's when SPLat/PC is first launched. After that it is affected only by simulated shadow memory instructions. That means you can stop and restart a simulation and the shadow memory data is retained. There is no simulation of an unsuccessful ShadowWrite or ShadowClear.

Previous Topic

Next Topic

In This Chapter

HD8: Permanent memory

HD8: Shadow memory

HD8: NVEM0


HD8: Permanent memory

Permanent memory in the HD8 is implemented in the main FLASH memory. By means of a proprietary circular re-use technique we achieve an endurance of 640,000 write cycles. The write and read functions are both very fast. The instructions that access permanent memory are PermStore and PermRecall.

Data in Permanent memory is reset to all zeros when a program is downloaded to the board.


HD8: Shadow memory

Shadow memory in the HD8 is implemented using main FLASH memory. Endurance is 10,000 writes or clears in total. ShadowWrite takes up to 20mS per byte. ShadowClear takes up to 10mS per byte. During these times the SPLat processor can do nothing else (for example quadrature counting). We use a smart algorithm to avoid redundant writes and clears, making the process as fast as possible and reducing wear on the memory.

ShadowRead is very fast.

Downloading a program to the controller does not change the contents of shadow memory.


HD8: NVEM0

NVEM0 (page 0 NVEM) in the HD8 is part of the same memory where your application program is stored. In fact, your program and NVEM0 share the same contiguous block of memory, called User Flash. The bigger your program, the less is available for NVEM0, and visa versa. In the HD8 there is approximately 21K of user flash. The exact amount is reported in the SPLat/PC Module window when you connect to a board. The amount will reduce in future Firmware versions as we use more of the total flash memory to implement new features. If you have developed a stable product and find a new firmware version has less memory you can always reFlash it back to the firmware you initially used to develop your application.

In the following description for the HD8, the block size is 512 bytes and the endurance is 10,000 writes (this varies from product to product).

The endurance figures given above are the chip maker's worst case ratings over all legal temperatures and supply voltages. They are very conservative but no guarantee is given that the chips can survive more writes. Exceeding the FLASH memory write endurance will be construed as mis-use and void our product warranty.

Boards with language dialect 15 or later implement NVEM0. This page provide details specific to the implementation in the SL99/100, MMi2xx, MS12x, CC16/18, RFD18, and HD8. General NVEM0 documentation is located elsewhere.

NVEM0 in these controllers is implemented using the processor's main FLASH memory. This memory can only be written in fixed size blocks. The block size differs between products.

What we have done, therefore is to create some "smoke and mirrors" to simulate byte-writable memory. That means your program can write to individual bytes, and they will be updated correctly. However, there are downsides:

  • Every time you write one byte all the bytes in the same block must also be written. That has a serious consequence for the endurance of the memory. Endurance refers to the unavoidable fact that FLASH memory wears out after a certain number of writes. What this means is that you must be very careful when using NVEM0 not to write to it too often.

    Our Firmware is smart enough to know that several bytes being written from one instruction only requires one single block write. Hence, writing a multi-byte record or a floating point number only counts as one "hit" on the endurance, but it does hit every byte in the block. But so does writing one single byte!
  • Because the processor must write a whole block at a time (actually, erase and then re-write the whole block), writes to NVEM0 are quite slow. Also, there is no way you can know where the block boundaries are relative to your program, so any write operation involving more than 1 byte could cross a boundary and therefore require 2 writes.

Whenever you change you program, and hence its size, the absolute addresses of all your NVEM0 data will change. That means a particular multibyte write operation could suddenly wind up straddling a block boundary and take twice as long. Be very cautious when evaluating the effect of write times on your program.

  • NVEM0 is located in the processor's Flash memory, directly after your program. There is a big danger in reading, erasing, modifying and re-writing the block that contains the end of your program and/or any block containing read-only tables. If the power should fail during the write you will not only lose your data, you will also damage your program (or tables) and most likely wreck your product. For this reason this NVEM0 implementation will not permit writing to the memory block that contains the end of your program. If that is attempted the board will suffer a fatal runtime error. You should therefore make sure that there are at least one block's worth bytes of not-written-to NVEM immediately before any NVEM0 that gets written to by your program. The best policy is to place all read-only tables directly after you program, then to pad out with one block of unused NVEM0.

Examples:

;Example 1: Padding with unused space, because I have no read-only tables:

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM page 0 memory

           NV0Space     XXXXX      ;Force new XXXXX byte block

NVData     ;Start defining any writable NVEM0 areas here

********* ********* ********* ********* ********* ********* ********* *********

;Example 2: Place read-only lookup tables first, then force a new byte block.

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM

;Table of sines 0-90', 10' increments (takes 40 bytes)

SineTable  NV0fNum      0          ; sin(0)

           NV0fNum      1.74E-1    ; sin(10)

           NV0fNum      3.42E-1    ; sin(20)

           NV0fNum      5E-1       ; sin(30)

           NV0fNum      6.43E-1    ; sin(40)

           NV0fNum      7.66E-1    ; sin(50)

           NV0fNum      8.66E-1    ; sin(60)

           NV0fNum      9.40E-1    ; sin(70)

           NV0fNum      9.85E-1    ; sin(80)

           NV0fNum      1          ; sin(90)

Padding    NV0Space     XXXXX      ;Force new block

;Any NVEM that gets written during program execution gets placed below this point.

;That would typically be large parameter tables that can be changed by an end-user.


HD8: OnBoard Counter type B

The board incorporates an onboard counter type B (called OBCB). This is a medium speed Firmware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or wind speed using a rotating vane anemometer.

The OBCB exists on several SPLat boards. Generic documentation for the feature is provided in the OnBoard Counter type B guide.

The OBCB is implemented on inputs 0 though 3. The maximum count frequency is approximately 1.8kHz, assuming a perfect square wave input. The accuracy is approximately 2% (this varies from product to product, depending on whether they use a crystal for timing or not. This board does not have a crystal).


HD8: OnBoard Quadrature Counter

The HD8 implements an OnBoard Quadrature Counter (OBQC) on inputs 0 and 1. A quadrature counter decodes the dual channel outputs of a quadrature encoder and maintains an up/down counter. A quadrature encoder is a (usually rotary) encoder that produces two output signal that indicate the amount of rotations and the direction of rotation.

You should make sure that the encoder cannot generate input state changes more often that 400uS apart. You should also make sure your program is designed to never perform any writes to non-volatile memory when it must track an encoder with the OBQC.

For more information see the generic OBQC documentation.


HD8: CPU device

The HD8 with late Firmware revisions can respond to UV instructions that address the CPU device. Some of the responses are dependent on the hardware configuration.

Board Type

The board will respond to the following board-type poll:

  SPxPoll2      3,!CPU

by placing its board type identification number on U0 and U1. The response is a function of the type (model) of board and the number of connected XPice expansion boards (e.g. XBIO16, XIRO16, HIO16). The numbers can be fetched from U0 and U1 by the PushU instruction.

The following table defines the responses for the HD8 with varying amounts of expansion:

Configuration | U0 | U1; HD8
Controller alone | 0 | 141
Controller+16XPice | 0 | 142
Controller+32XPice | 0 | 143
Controller+48XPice | 0 | 144
Controller+64XPice | 0 | 145

Note that you can discover in this way how much expansion has been added but not the type of expansion board.

Interpreting the number of XPice points can be a little tricky. For boards with segregated inputs and outputs, like XIRO16 and HIO16, each 8I/8O board corresponds to 16 XPice points. For the XBIO16, each bidirectional point corresponds to one input and one output (and appears as such on the SPLat/PC screen). That means one XBIO16 board gets counted as 32 XPice points although it only adds 16 I/O points to your configuration.

PWM base frequency

The HD8 can be supplied to special order with pulse width modulated (PWM) outputs in lieu of analog outputs. Normally the frequency of the PWM pulse train is 16kHz. The base frequency can however be altered using the instruction:

SPxCmd1          0,!CPU

with register U0 preset to a value between 0 and 7. The resulting PWM frequencies are given in the following table:

U0 | PWM frequency
0 | 16kHz (Default)
1 | 8kHz
2 | 4kHz
3 | 2kHz
4 | 1kHz
5 | 500Hz
6 | 250Hz
7 | 125Hz

Any other value will result in a fatal runtime error.

Both PWM pins are affected. This feature is designed to be used with PWM output circuits such as DC motor speed controls or RC servos.

When used with an AnOut instruction, the resolution is 8 bits. With fAnOut it is 10 bits.

Modify SuperTimer 10mS clock interval

You can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.


HD8: Option links/Xwire address setting

There are two links on the HD8 board. They are labelled A0 and A1, and can be read by your program as inputs 7 and 8. The primary intended use is for setting the Xwire slave address of the board. Alternatively you could use them in your program to select end-user configuration options.


HD8: Comms connector

The COMMS connector works at 5V CMOS/TTL levels. For programming you need to use a special programming cable part number PC232. This is the same cable used with the CC18, MS120, and numerous custom controller.


HD8: Communications

The HD8 implements 3 serial communications protocols: Our proprietary SPLatLink, the industry standard ModBus RTU (master or slave), and the "User programmable protocol".

These protocols use the programming (serial) port. This is a "TTL compatible" serial port. It requires a TTL to RS232 converter to convert it to industry standard RS232. Our PC232 programming cable is suitable for this. With the programming cable it will connect directly to any PC that has an RS232 (COM port), or to any other device that has an RS232 "DTE" port. The maximum cable length is 5m. You can also use a TL485 adaptor to convert to RS485, which can go hundreds of meters.

User programmable protocol

This refers to the ability of SPLat to be programmed to send and received arbitrary characters. More information.

ModBus RTU Slave

ModBus is the oldest and most widely accepted protocol in automation. This controller supports ModBus RTU slave mode. In the initial implementations the ModBus feature allows message lengths up to the ModBus limit of 255 bytes. This may be reduced in the future as we add new features and have to "take back" some of the memory. In practice a 64-byte message can transfer 57 bytes of data.

More information on ModBus.

ModBus RTU Master

This controller supports ModBus RTU Master mode.

SPLatLink

SPLatLink is our proprietary protocol for communicating with a SPLat controller while it is running your SPLat Program.

SPLatLink is deprecated. We discourage its use in custom applications.


HD8: Xwire

The HD8 supports Xwire. Xwire is a method of connecting several boards together into a multiprocessor network.

The Xwire connections are on connectors X1 and X2. Jumpers A0 and A1 can be used for setting the board's Xwire address via your program, if it is to be a slave. See XwireSetAddr

Xwire documentation.

The maximum number of data bytes that can be transferred into or out of the HD8 via Xwire is 32.

Electrically the HD8 can drive 10 unit loads and represents one unit load. That means one HD8 board can drive 10 other HD8s or other "unit load" boards.

The master configuration table may contain a maximum of 50 entries. This is more than the hardware limit. Conceivably one slave board might have more than one logical device (address) on it.


HD8: Programming

The HD8 is a member of the SPLat family of controllers. That means that it shares its programming language and programming tools with all other SPLat controllers. Whatever you learn on the HD8 is applicable to other models. The only differences will be those relating to particular hardware resources and features, and dialect differences. "Dialect" is a term we use to describe differences between versions of the SPLat language, which really means that we are continually adding new language features which are not available on older controller products.

You program the board by writing and debugging a program to suit your application in your PC. You then download the program to the board and complete debugging and overall system testing. Once the program has been downloaded to a SPLat board it will remain secure in the board. You can download a new program to the board at any time, in which case the old one is overwritten. You cannot upload a program from a board back into your computer, so it is impossible for a competitor to steal your program out of a board.

The PC software you use for programing is called SPLat/PC. SPLat/PC gives you a wide range of programming and debugging tools. It incorporates a help file with reference material and our classic, highly acclaimed, programming tutorial. You can download SPLat/PC free from here.

SPLat/PC also features a series of semi-interactive "mini-tutorials". These are designed to give you a bit of a kick start with SPLat. You should start your exploration there.

The proprietary SPLat programming language contains a repertoire of over 400 instructions. These instructions cover a huge range of functions. The instruction set differs from conventional programming languages and microprocessor assembler languages in that it has a strong emphasis on the needs of control applications. In particular, there is a subset of just 14 instructions called FastTrack. The FastTrack subset contains all you may ever need in order to implement simple timing and sequencing applications with batch counting and multiple interlocks.

In December 2005 we introduced MultiTrack programming. This is an extremely easy method of writing programs that can (seemingly) do several things at once. (For experts: MultiTrack is a non-preemptive multitasking scheduler that is extremely easy to learn and use). MultiTrack extends FastTrack, so that with just 16 instructions you can write useful multitasking programs. The mini-tutorials in SPLat/PC include an introduction to MultiTrack. It has been our experience over many years that a lot of people shy away from MultiTrack because multitasking is a large conceptual leap. The MultiTrack mini-tutorial is excellent in that regard, as it presents the concept in an extremely approachable way. Even if it takes you several hours to wrap your head around, invest the time! The investment will pay off handsomely in anything but the most trivial program. After you have grasped the basics of MultiTrack, your next port of call should be the more detailed MultiTrack tutorial.

The 400+ instructions that comprise the full SPLat programming language are documented within the SKB in the Programming Reference. Some of these are instructions we have introduced to simplify common control tasks even further, such as blinking an LED, and form an extension of the FastTrack concept. Therefore, it's a good idea to look there once you move on from pure FastTrack.

If you need even more knowledge you can work with the tutorial built into the SPLat/PC help file. It starts with a FastTrack tutorial that approaches FastTrack that is different from the approach taken in the online course (this is older material), then goes into the "classic" tutorial. The classic tutorial starts off with the nitty gritty details of the internal registers in SPLat. It then works up from there with a detailed expose, along with many examples, of the vast majority of SPLat instructions and how they are used.

Note: The HD8 requires a special adaptor cable for programming. This is supplied with the HD8DK developer's kit or may be purchased separately (PC232).

You can also program this controller in C. Click here for details.

Previous Topic

Next Topic

In This Chapter

HD8: Programming cable


HD8: Programming cable

The controller needs to be connected to a PC for program download. The full process of connecting to the PC is described in the help file that comes with SPLat/PC, our programming software.

For this controller you need a special SPLat programming cable, PC232. This cable is supplied with the Developer's Kit, and may also be ordered separately through our website. The cable plugs into a 9-pin serial (COM) port on a PC.

You also need a computer with a serial (COM) port. Modern notebook computers tend not to have COM ports. In that case you will need a USB to Serial adaptor. Not all such adaptors work equally well. The ones we find work are based on the FTDI chipset. These are available on our website.


HD8: Operating temperature ratings

The maximum allowable operating temperature of the board is governed by several factors.To determine the highest permissible operating temperature you need to evaluate each factor separately and then use the worst (lowest temperature) answer.

The factors that must be taken into account are:

  1. The semiconductors on the board have a maximum ambient temperature rating of 70°C.
  2. The chip driving the outputs will also self-heat, and eventually fail if run too hot for too long. Evaluating this factor is discussed under HD8: Bidirectional points used as outputs
  3. Hot components fail faster than cool components. For semiconductors, lowering the temperature by 10°C doubles the life time.

The minimum operating temperature is -40°C with the proviso that there be no water condensation. Conformally coated boards can be supplied to special order to minimize the effects of condensation.


HD8: Firmware revisions and reFlashing

This chapter contains the two related topics of Firmware revisions/updates and reFlashing.

Previous Topic

Next Topic

In This Chapter

HD8: reFlashing

HD8: Firmware version history and updates


HD8: reFlashing

reFlash is the ability of a SPLat board to have new operating Firmware loaded into it. The operating firmware is the program that we load into the board, that gives it its SPLat "personality". This is quite different from the program you write to tailor it to your application, which we call SPLatWare.

reFlash is available in 2nd generation boards, starting with the MMi99 with firmware version 3.3 or later (reFlash was developed after the earlier MMi99 versions were released). Older designs, which do not have reFlash, are SP108, SL88, MMi88, AJ18, PMC3, CA20, OEM32 and OEM36.

There are three reasons why you would ever want to reFlash a SPLat board:

  1. Because we introduce new firmware features quite frequently, it has become impossible to ensure that all our (and our dealers') inventory is fully up to date. Therefore it is possible that a board you receive does not contain the "latest and greatest". Thanks to the miracle of reFlash technology you can still update your board to the latest version.
  2. We goofed, and there's a bug in the existing firmware. For example a SPLat instruction giving incorrect results.
  3. We devised a new feature (for example new instructions) and you'd like to upgrade your existing board(s). This makes most sense if you keep a board for development. There is little point in upgrading boards already shipped in your product, unless you are also upgrading the SPLatWare and you have used new language features.

reFlashing is discussed more thoroughly elsewhere in the SKB.


HD8: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for HD8. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date | rfl file | Description
V3.11; May11 | N/A | First release for general sale, dialect 26
V3.0 | N/A | Initial release, dialect 18 as a "special"


SL100 product documentation

Entry level pricing, up scale performance!

Congratulations on your choice of a SPLat controller. SPLat gives you the ability to implement a sophisticated control system in your product easily and inexpensively, and will give you an edge that will have your competitors wondering whatever hit them!

The SPLat controller gives you a feature set rivalling competing products costing many times as much.

  • Easier to program than C, Basic or ladder;
  • Can easily be "private labeled";
  • Large program memory (over 20K bytes, approx 12,000 instructions);
  • Capable of sophisticated control functions including math, state machines, PID, algorithms, heuristics and lookup tables;
  • Built in multitasking;
  • Real-world I/O: 400mA outputs and ruggedized inputs;
  • Expandable: Extra I/O, special function add-ons (including analog) and custom functions.

Have your first working program in less than an hour!

SPLat controllers are extremely easy to get started with. Our programming software is free, and includes a full simulator that allows you to test your program and explore possibilities without any hardware present. Our programming language has been skillfully crafted to make the simple tasks absurdly easy to program, and yet there is a rich repertoire of advanced instructions to satisfy the sophisticated user. Our training materials are legendary (and free). The controller contains real world input/output circuits, so you won't have to design your own.

How do we do it?

You may be sceptical about how we can deliver so much performance at such a budget price. When we design a controller we aim for the best possible value for money. We design and re-design our circuitry to eliminate unnecessary or expensive components. As far as possible we select multi-vendor components so we can shop around. Maybe most importantly, we are engineers designing for end users, not engineers designing for engineers. We don't allow ourselves to be carried away by megahertz and gigabytes. We understand that what is really important to an OEM operating in the real world is getting the necessary performance at a price that will give a competitive advantage.

One thing we are obsessive about is build quality. We hate returns, and get very few of them (way less than 1%). Every board we make is built with strict process controls. Every board is visually inspected. Every board is 100% functionally tested. And every board is backed by our 2-year standard product guarantee and Post-Nuke repair policy.

The SL100 supersedes the SL99. It has all the same features but higher specifications (faster, more program memory).

Previous Topic

Next Topic

In This Chapter

SL100: Introduction

SL100: Making external connections

SL100: Power supply

SL100: External inputs and outputs

SL100: Non-volatile memory

SL100: Onboard counter type A (OBCA)

SL100: OnBoard Counter type B

SL100: OnBoard Quadrature Counter

SL100: CPU device

SL100: Option links

SL100: SPice connector

SL100: Xwire

SL100: Comms connector

SL100: Communications

SL100: Programming

SL100: MultiTrack

SL100: Operating temperature ratings

SL100: Firmware revisions and reFlashing


SL100: Introduction

This document covers the SL100 and its predecessors the SL88 and the SL99. All the information has been re-worded to match the SL100. The various models come about basically as a result the availability of more capable versions of the microcontroller chips we use. In most cases everything gets significantly better, though occasionally there may be a minor compromise on a feature. In the case of the SL100 it has less endurance in the Shadow Memory.

The following table highlights the major differences between the products. What is not listed is very many features that are implemented in successive dialects.

Item | SL100 | SL99 | SL88
Introduced | 2007 | 2002 | 1997
Analog | 1 in, 1 out, 10-bit | 1 in, 1 out, 8-bit | No
Relative speed (approx only) | 20x | 7.5x | 1x
MultiTrack | Yes | Yes | No
Program memory | 25K | 9K | 4K
RAM | 250B | 250B | 64B
Expandable | Yes | Yes | No
SPice expansion | Yes | Yes | No
Shadow memory | Yes | Yes | No
Shadow memory endurance | 10,000 writes total (Less than SL99!) | 10,000 writes on any byte | N/A
NVEM | Yes | Yes | No
NVEM0 block size | 512 | 128 | N/A
Active RTS signal | Yes | No | No
Connections | Plug in | Plug in | Screw terminal
Power consumption | Slightly higher than SL88 |  |

Previous Topic

Next Topic

In This Chapter

Getting started with your controller

SL100: Using this document

Other resources

SL100: Product overview


SL100: Using this document

This document concerns itself primarily with information that is specific to the SL100 product. In particular, it concentrates on the hardware aspects of the SL100. Because programming is uniform across all SPLat products, all SPLat programming information is located elsewhere. For more introductory information on programming please see SL100 Programming within this document.

The material is organized largely according to various hardware features. This should allow you to quickly locate the information you need in order to properly match the SL100 to the outside world and make electrical connections. There are also a couple of chapters on more general topics like determining the permissible operating temperature.

We suggest you start with a quick read through the whole document, to familiarize yourself with what's there. After that you should be able to use it as a technical reference to look up specific information you need.

Most technical product manuals contain a Specifications section. We've done it a bit differently (we do many things differently, which is probably why you are using SPLat!). The numbers and ratings are discussed as part of each feature. For example, input voltages and currents are discussed under SL100 External digital inputs. While this may seem to make the information a little harder to locate (compared to a consolidated table), it gives us the ability to loosen up a little and discuss how the operating envelope can be flexed under different conditions. For example, the allowable output current depends on the operating ambient temperature.


SL100: Product overview

The SL100 is a SPLat single board programmable logic controller that includes a number of innovative features. Key features are:

  • 8 digital inputs;
  • 8 digital outputs;
  • 1 versatile analog input (up to 5 with an add-on SPice expansion board);
  • 1 analog output (up to 3 with an add-on SPice expansion board);
  • Provision for modem communications;
  • An extremely flexible range of expansion possibilities.

The SL100 is supplied as two different packages.

  • SL100DK. This is the Developer's Kit version. It includes a set of matching connectors and software on CD;
  • SL100OEM. This is the SL100 board itself, sold on its own with a set of matching connectors but no other accessories. It is intended for OEM customers.


SL100: Making external connections

Previous Topic

Next Topic

In This Chapter

Some common terminology

SL100: Where to get matching connectors


SL100: Where to get matching connectors

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.


SL100: Power supply

The SL100 needs a suitable DC power supply connected to the 2-pin "PWR" connector. The positive terminal of the power supply must connect to the pin labeled "+" and the negative terminal must connect to the pin labeled "0V". Please see the article on common terminology for an explanation of the labeling.

Supply voltage

In general, the SL100 needs a supply voltage between 12VDC and 24VDC. The voltage is allowed to go to a maximum of 32V. Note that the SL100 operating temperature ratings depend on the supply voltage.

It's quite OK to use an unregulated power supply. Just remember that the ripple will make the voltage instantaneously less than the average DC value, and the board will respond to the instantaneous value. Remember, too, that an unregulated supply can go quite a bit higher than its nominal value, especially when lightly loaded and if the mains voltage is high.

If you are not using the analog functions, you can operate the board down to 10VDC. That means it can for example work off a 12V lead-acid battery (though in a vehicle it may not function during starting, when the battery can get dragged down to well below 10V).

The thermistor function contains a programmable drive voltage that can go up to 20V. If you are using that function you must use a supply voltage at least 2V more than the thermistor drive voltage you are going to program.

It is quite possible for add-on boards to impose additional constraints on the supply voltage. Please refer to the add-on board documentation.

Supply current

The current drawn by the board will depend on the supply voltage, what the board is doing and on any add-ons. Allowing 5mA for a SPice board, maximum supply current draw versus supply voltage is:

Voltage | Max current
10V | 90mA
12V | 110mA
15V | 125mA
18V | 140mA
24V | 175mA
28V | 195mA
32V | 220mA

These figures are calculated based on everything being on at once, including all indicator LEDs and a full 5mA load on the analog input. The figures do not include any external loads (except analog) such as relays.

Note: Figures at 28V and 32V are given for reference only. We do not recommend operating the board at voltages over 24V for sustained periods of time. The board can be damaged if the power supply voltage goes over 32V, even momentarily.


SL100: External inputs and outputs

Previous Topic

Next Topic

In This Chapter

SL100: External digital inputs

SL100: External digital outputs

SL100: External analog input

SL100: External analog output


SL100: External digital inputs

The above picture shows the two (digital) input connectors and a couple of typical input devices.

Each connector contains the connections for 4 inputs, plus a common pin that connects onboard to the power supply 0V. The common pin on each input connector is provided simply as a convenience to you when wiring to the board.

In the picture A is a simple switch connected to input 6. B is the output of an NPN style sensor connected to input 0. The sensor would also require a positive supply voltage to power it. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

The input is ON when the input pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - (V(supply) - 2V) / 10000

The leading minus sign simply means the current is flowing out of the pin. The 2V represents the voltage drop of the input indicator LED. The 10000 represents the 10K input pullup resistance.

This equation translates to about 1mA with a 12V supply and 2.2mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognise the input as ON when the voltage on the input pin is typically less than 2.3V (1.5V worst case). That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input, as defined by the processor chip, is approximately 2.3V or a little less. However, the chip manufacturer (Motorola) only guarantees that an input will be treated as low (in our case ON), if the voltage is below 1.5V. Similarly, the chip is guaranteed to treat as high (OFF) only voltages above 3.5V. The range between 1.5V and 3.5V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

Note that the threshold voltage varies from product to product. Don't assume that what applies to this board will apply to others.

The external inputs have a built in hardware noise filter for improved compliance with electromagnetic emission requirements. The input filter introduces a delay in the input response time. The delay times are different for OFF and ON, and vary with the supply voltage. Turn-on time is less than 1µS. Turn-off time is approximately 500µS divided by the supply voltage, i.e. 42µS at 12V and 21µS at 24V.

How to blow up the inputs

You can blow up the inputs by:

  1. Applying a positive voltage 5V above the positive supply. This can damage the indicator LED instantaneously by reverse voltage breakdown;
  2. Applying a negative voltage of more than (32V - The positive supply voltage). This will damage the input pullup resistors due to overheating. This is not a sudden death failure. The pullup resistors are rated at 200mW.


SL100: External digital outputs

The adjacent picture illustrates the (digital) output connectors and some typical loads.

The 8 outputs are brought out on two 5-pin plug-in connectors. Each connector has a "convenience" pin connected to power supply positive. The output load should always be connected between the output pin and power supply positive. Use the convenience pin in preference to wiring directly to the power supply +.

CAUTION: Take care never to short an output pin to the positive common pin. If the output is on at the time you will blow up the output chip instantly!

The loads illustrated are:

A: The input pins of a solid state relay. Note the polarity!

B: A relay coil. Notice we have included a catch diode across the coil. While there are diodes on the board we recommend a diode across the coil, as close as possible to the coil, to minimize problems with switching noise. A suitable diode is a 1N4001 or similar 1A/50V rectifier diode.

C: An LED, with a series current limiting resistor. Resistance calculators

The (digital) outputs are implemented using the ULN2803A, a chip that contains Darlington transistors. The onboard indicator LEDs are connected directly to the output pin, and returned to the positive supply via (typically) 10K current limiting resistors.

The outputs consist of transistor switches between the output pin and 0V. When the output transistor is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage. If you set the multimeter to 200mADC, the output LED will turn on and you will get a reading of approximately V/10 milliamps, where V is the supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output chip will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built into the chip. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the relay coil as close as possible to the relay, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

If you connect the loads (for example relays) separately to the power supply or to a separate power supply, it may be possible to remove the controller's supply connection and still have power fed to the controller via the loads and the onboard protection diodes. This can produce confusing or possibly even dangerous results if the controller gets enough of this "sneak" power to run. It is safer to use the convenience + supply pin on the connector to provide positive voltage to the load.

The maximum allowable output current is 400mA steady state, with brief (100mS) peaks of 500mA allowed. There is also a limit to how much total current one ULN2803A chip can handle. This relates to the internal heating of the chip.

Let's look at 2 extreme cases:

  1. All 8 outputs on one chip conducting the same current
  2. The allowable current concentrated in a few outputs.

Equal currents

The following table lists allowable current versus ambient temperature, when all 8 outputs are conducting the same current at once:

Temperature | Allowable current per output
25°C | 180mA
40°C | 160mA
50°C | 145mA
60°C | 130mA
70°C | 115mA

Concentrated currents

The following table shows the maximum allowable currents, when concentrated in a few outputs, versus temperature

Temp | 1st O/P | 2nd O/P | 3rd O/P | 4th O/P
25°C | 400mA | 400mA | 300mA | 0
40°C | 400mA | 400mA | 150mA | 0
50°C | 400mA | 400mA | 0 | 0
60°C | 400mA | 325mA | 0 | 0
70°C | 400mA | 220mA | 0 | 0

Naturally there are many other possible combinations, but the above should serve as a guide.

The above figures are intended as a guide only. In any event, the absolute maximum operating temperature for the ULN2803A chip is 70°C.

If you feel like doing the actual sums, here's the procedure.

  1. For each output, calculate its output voltage drop from the formula
    V(out) = 0.92 + 1.85 * I(out) [ I(out) in Amperes ]
  2. For each output calculate the dissipation in watts from the formula
    P=V(out) * I(out)
  3. Add up all the dissipation figures to give P(tot) in watts
  4. Calculate the maximum allowable ambient temperature from the formula
    Ta(max) = 125 - P(tot) * 55
  5. If you get an answer over 70°C then use 70°C

This calculation aims at a maximum internal (junction) temperature of 125°C, which is the maximum rating of the chip. There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better. The ULN2803A data sheet contains additional rating curves.

When semiconductor switches fail they are more likely to become stuck ON than OFF.

Note: This discussion pertains to the maximum operating temperature for the ULN2803A output chip only. See SL100 Operating temperature ratings for a further discussion.

How to blow up the outputs

Here are some of the ways you can blow up the outputs:

  1. Connecting the output pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than the output current rating)
  2. Connecting the output pin to any negative voltage
  3. Connecting the output pin to any voltage greater than the positive supply.
  4. Switching a load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  5. Using an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.

Driving an incandescent lamp. Incandescent lamps draw a warm-up surge current of many (up to 20) times their rated steady state current.


SL100: External analog input

SL99 Analog connector

The picture above shows the connector used for analog inputs and outputs, and the associated configuration jumpers.

There is one analog input channel on the SL100 board. It can be configured to operate on 0-5V, 0-10V, 0-20mA or as a temperature measuring input (using an external thermistor sensor). In all cases the input uses one input pin and one common pin. The common pins on the analog connector are tied to the board's 0V terminal. Please see the article on common terminology for an explanation of the 0V terminal.

The analog input is designated as analog A in AnIn instructions or 0 in fAnIn instructions. It has a 4-pin configuration jumper field. A single jumper (shunt) on a pair of jumper pins selects the operating mode (V=voltage, I=current or T=temperature).

Using voltage mode

To use an analog input in voltage mode you can either

  • set its jumper to bridge the two pins either side of the letter V for 0-10V range or
  • remove the jumper entirely for 0-5V range,

and apply the input voltage between the input (positive) and the common (most negative). When the AnInA instruction is executed you will get a number between 0 and 255 in the X-register. 0 represents 0 input and 255 represents full scale.

From dialect 18 you can also use the fAnIn instruction. This will return a normalized floating point number in W which ranges from 0 at 0V in to 1.000 at full scale in. The range 0 to 1.0 is divided into 1023 steps of 0.00097752. fAnIn selects the required channel with a numeric argument, 0, for input A. The channel number is jndexed.

In voltage mode the input resistance is 200KOhm. The accuracy is about 1.5% of full scale and resolution is 10-bit (0.1% of full scale). There is an input noise filter comprising a single RC time constant of 5mS.

If you leave the jumper out, you will get a 0-5V range instead of 0-10V.

Using current mode

To use the analog input in current mode you set its jumper to bridge the two pins either side of the letter I. You then feed the current to be measured into the input pin, with the return out of the common pin. The current source (e.g. 4-20mA transmitter) must be of the type that sources a positive current from its output pin and sinks (returns) the current into its ground (or minus out) pin. Most 4-20mA transmitters are "line powered" and have a simple 2-wire connection. Connect the positive pin of the transmitter to the positive supply voltage and the negative pin to the SPLat input pin.

Some 4-20mA devices are non-loop powered. These have 3 connection wires: Positive supply, ground/negative supply and output. To connect such a device to the SPLat provide it power to its positive and ground pins, connect its ground pin to the SPLat 0V and its output to the SPLat analog input.

When the AnInA instruction is executed, you will get a number between 0 and 255 in the X-register. 0 represents 0mA and 255 represents 20mA. At 4mA you will get a reading of 51 (plus or minus any conversion error). You can also use fAnIN, which will return 0 for 0mA, 1.0 for 20mA and 0.2 for 4mA (plus or minus any conversion error).

In current mode the input resistance is 250Ohms. The accuracy is about 1.5% of full scale and resolution is 10-bit (0.1% of full scale). There is an input noise filter comprising a single RC time constant of 10mS.

Using thermistor mode

To use the analog input in temperature mode you set its jumper to bridge the two pins either side of the letter T and connect a suitable thermistor between the analog input pin and common. There are two basic types of thermistor: Positive Temperature Coefficient (PTC) and Negative Temperature Coefficient (NTC). The latter, NTC, is the type used for temperature measurement with SPLat. Typically, an NTC thermistor is specified as having a certain resistance at 25 degrees C.

The adjacent diagram shows the thermistor measuring circuit.

MMii99 thermistor circuit

The thermistor is the bottom resistor in a voltage divider. The top resistor is a fixed onboard 10.25KOhm resistor. The voltage divider is driven by a programmable voltage that appears as analog output 6. This is an "onboard output" that is used for thermistor drive and is also available on the SPice connector, but does not "use up" the externally available analog output. The thermistor drive voltage can be set to anything between 0V and 20V. Note that the board supply voltage must be at least 2V greater than the intended drive voltage. The drive voltage is programmed using an fAnOut 6 instruction. With W=0 this will result in 0V out, with W=1 it will result in 20V out.

In this thermistor mode the full scale analog input voltage is 5V.

A thermistor is an electronic component that changes resistance with temperature. Don't confuse this with an RTD sensor, which also changes resistance with temperature but is much less sensitive! The warmer an NTC thermistor gets, the lower its resistance. Hence, a reading of the input voltage can be used to calculate the thermistor temperature.

Accuracy and resolution depend on the thermistor you use and how you program it. The best achievable figures are about 0.05°C for both, if the system includes provision for software calibration. The SPLat Knowledge Base contains a tutorial on thermistor temperature measurement using SPLat boards. As a first approximation, when shopping for a thermistor for your application, shoot for one with a resistance of 10KOhm in the middle of the temperature range you need to cover.

How to blow up the analog input

Here are some ways of blowing up the analog inputs:

  1. In any mode, connect the input to a voltage greater than the positive supply or negative with respect to the 0V terminal.

In current mode, apply more than 7.5V to the input. This will overheat the current shunt resistor and lead to loss of accuracy or, in the extreme, damage the board.


SL100: External analog output

SL99 Analog connector

The picture above shows the connections for the external analog input and output. The analog output is designated analog output B. It can generate a 0-10V signal, with a maximum load current of 5mA, corresponding to a 2KOhm load. The output has internal current limiting (which is not the same as being short circuit proof!).

The analog output voltage is a positive voltage measured with respect to the 0V terminal. It is programmed using the AnOutB instruction. With X=0 this will result in 0V out, with X=255 it will result in 10V out. You can also use fAnOut 1 which will result in 0v for W=0 and 10V for W=1.

The output resolution is 8 bits (256 steps between 0 and full scale) when you use the AnOut# instructions and 10 bits when you use fAnOut. The output is generated as a filtered pulse width modulated (PWM) signal. The filter consists of a simple 100mS RC low pass filter. For a full scale step output change, say from 0V to 10V, this will take 230mS to settle to within 10% of the full change, 450mS to settle to 1% and 550mS to settle to 0.4%.

How to blow up the analog output

Here are some ways of blowing up the analog output:

  1. Connect the output to any positive voltage over 10V and program a 0V output.
  2. Short the output to 0V for extended periods of time (it might survive)
  3. Connect the output to a negative voltage, even momentarily.


SL100: Non-volatile memory

The term non-volatile memory means a form of memory that does not lose its data when the power goes off. It can be implemented using a variety of technologies. Your hard disk drive is non-volatile. The magnetic core memory used in computers in the 60's and 70's was non-volatile. Some systems use a small battery to keep the memory powered up even when main power is off. The technologies commonly used in SPLat boards are known as "EEPROM" and "FLASH" memory.

The board contains two kinds of non-volatile memory: "Permanent" memory and "Shadow" memory. The Permanent memory is the type that has existed in SPLat boards almost from the very beginning. Shadow memory is a much newer development made possible by advances in microcontroller technology.

Permanent memory

This type of non-volatile memory was introduced into SPLat in March 1998. It allows just 13 bytes of RAM (data memory) to be saved into a non-volatile memory as a indivisible (all or nothing) block to be later restored. The two instructions for this are PermStore and PermRecall. The data does not survive a new program download.

Shadow memory

This is newer type of non-volatile memory, introduced in March 2002 in language dialect 10. It allows all of RAM to be selectively backed up to a "shadow" memory in blocks of any size, and likewise to be restored in blocks of any size. It also allows the shadow memory to be cleared in blocks, and supports indexed addressing for more sophisticated data management in larger projects.

The term shadow memory comes about because it is a region of non-volatile memory that overlaps the RAM. When a certain RAM location is written to the shadow memory, it is written to the same byte address within the shadow memory as its own RAM address.

The instructions for shadow memory are ShadowWrite, ShadowRead and ShadowClear. All these can be used as indexed as well as non-indexed instructions.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

Comparison between permanent and shadow memory

There are a number of differences between the two types on non-volatile memory:

  • Permanent memory can only be written and read all at once, whereas shadow memory can be accessed in any block size.
  • Permanent memory has a more robust error checking mechanism. In permanent memory a checksum is written with the data, and if the checksum fails on readback an older version is automatically substituted (with a status signal returned in X). In shadow memory, the data is automatically read back immediately after write, and any error is signalled in the R-register. However, shadow memory has no mechanism for detecting a readback error at a later a time.
  • Depending on the specific SPLat model, and the underlying processor and memory technologies, the shadow memory can have significantly less endurance than permanent memory. Endurance is the number of write or erase cycles a memory location can perform before it becomes worn out.

Endurance of non-volatile memories

Certain non-volatile memories have a finite life, as defined by the number of writes or erases (clear cycles) they can endure. For permanent memory this translates to the total number of times a PermStore can be executed before the memory is likely to give erroneous data. For shadow memory it means the number of times any given byte address can be subjected to a ShadowWrite or ShadowClear before it is likely to give errors. You should take great care to not write a program that will write or clear the non-volatile memory so often that it gets worn out too soon.

Example: Say a given non-volatile memory has a stated endurance of 100,000 cycles, and your program counts cans of sardines at a rate of 100 per minute, updating the non-volatile memory after every count. Your board would only last 1000 minutes, or about 2 shifts. If you re-write your program so it only updates the non-volatile memory once per hour, you would get 100,000 hours or about 12 years life, with some loss of accuracy as the trade-off.

CAUTION: You must take into account the possibility of power failing right in the middle of writing to the non-volatile memory. Unless specifically stated for the SPLat product you are using, there is always a possibility that data will be corrupted if that happens. You must therefore ensure, by the design of your system, that there will be no disastrous consequences of corrupted data.

The Permanent Memory has the best protection in this regard. Data is written with a checksum, and the PermRecall instruction provides a result indicator in the R register.

Support in SPLat/PC

Both types of non-volatile memory are supported in SPLat/PC.

The support for permanent memory is documented in the SPLat/PC "classic" help file. In the help file index search for "Permanent memory".

SPLat/PC simulates shadow memory by saving the shadow data in the computer's memory. The data is initialized to all zero's when SPLat/PC is first launched. After that it is affected only by simulated shadow memory instructions. That means you can stop and restart a simulation and the shadow memory data is retained. There is no simulation of an unsuccessful ShadowWrite or ShadowClear.

Previous Topic

Next Topic

In This Chapter

SL100: Permanent memory

SL100: Shadow memory

SL100: NVEM0


SL100: Permanent memory

Permanent memory in the SL100 is implemented in the main FLASH memory. By means of a proprietary circular re-use technique we achieve an endurance of 640,000 write cycles. The write and read functions are both very fast. The instructions that access permanent memory are PermStore and PermRecall.

Data in Permanent memory is reset to all zeros when a program is downloaded to the board.


SL100: Shadow memory

Shadow memory in the SL100 is implemented using a byte erasable EEPROM memory. Endurance is 10,000 writes or clears total (across all bytes). ShadowWrite takes up to 20mS per byte. ShadowClear takes up to 10mS per byte. During these times the SPLat processor can do nothing else (for example quadrature counting). We use a smart algorithm to avoid redundant writes and clears, making the process as fast as possible and reducing wear on the memory.

ShadowRead is very fast.


SL100: NVEM0

NVEM0 (page 0 NVEM) in the SL100 is part of the same memory where your application program is stored. In fact, your program and NVEM0 share the same contiguous block of memory, called User Flash. The bigger your program, the less is available for NVEM0, and visa versa. In the SL100 there is approximately 21K of user flash. The exact amount is reported in the SPLat/PC Module window when you connect to a board. The amount may reduce in future Firmware versions as we use more of the total flash memory to implement new features.

In the following description for the SL100, the block size is 512 bytes (128 bytes in SL99) and the endurance is 10,000 writes (this varies from product to product).

The endurance figures given above are the chip maker's worst case ratings over all legal temperatures and supply voltages. They are very conservative but no guarantee is given that the chips can survive more writes. Exceeding the FLASH memory write endurance will be construed as mis-use and void our product warranty.

Boards with language dialect 15 or later implement NVEM0. This page provide details specific to the implementation in the SL99/100, MMi2xx, MS12x, CC16/18, RFD18, and HD8. General NVEM0 documentation is located elsewhere.

NVEM0 in these controllers is implemented using the processor's main FLASH memory. This memory can only be written in fixed size blocks. The block size differs between products.

What we have done, therefore is to create some "smoke and mirrors" to simulate byte-writable memory. That means your program can write to individual bytes, and they will be updated correctly. However, there are downsides:

  • Every time you write one byte all the bytes in the same block must also be written. That has a serious consequence for the endurance of the memory. Endurance refers to the unavoidable fact that FLASH memory wears out after a certain number of writes. What this means is that you must be very careful when using NVEM0 not to write to it too often.

    Our Firmware is smart enough to know that several bytes being written from one instruction only requires one single block write. Hence, writing a multi-byte record or a floating point number only counts as one "hit" on the endurance, but it does hit every byte in the block. But so does writing one single byte!
  • Because the processor must write a whole block at a time (actually, erase and then re-write the whole block), writes to NVEM0 are quite slow. Also, there is no way you can know where the block boundaries are relative to your program, so any write operation involving more than 1 byte could cross a boundary and therefore require 2 writes.

Whenever you change you program, and hence its size, the absolute addresses of all your NVEM0 data will change. That means a particular multibyte write operation could suddenly wind up straddling a block boundary and take twice as long. Be very cautious when evaluating the effect of write times on your program.

  • NVEM0 is located in the processor's Flash memory, directly after your program. There is a big danger in reading, erasing, modifying and re-writing the block that contains the end of your program and/or any block containing read-only tables. If the power should fail during the write you will not only lose your data, you will also damage your program (or tables) and most likely wreck your product. For this reason this NVEM0 implementation will not permit writing to the memory block that contains the end of your program. If that is attempted the board will suffer a fatal runtime error. You should therefore make sure that there are at least one block's worth bytes of not-written-to NVEM immediately before any NVEM0 that gets written to by your program. The best policy is to place all read-only tables directly after you program, then to pad out with one block of unused NVEM0.

Examples:

;Example 1: Padding with unused space, because I have no read-only tables:

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM page 0 memory

           NV0Space     XXXXX      ;Force new XXXXX byte block

NVData     ;Start defining any writable NVEM0 areas here

********* ********* ********* ********* ********* ********* ********* *********

;Example 2: Place read-only lookup tables first, then force a new byte block.

NV0BlockSize   EQU      XXXXX ;<<<<<<<< Fill in the block size for your controller

             ...

           Return                  ;The last program instruction

           NVEM0                   ;Start of NVEM

;Table of sines 0-90', 10' increments (takes 40 bytes)

SineTable  NV0fNum      0          ; sin(0)

           NV0fNum      1.74E-1    ; sin(10)

           NV0fNum      3.42E-1    ; sin(20)

           NV0fNum      5E-1       ; sin(30)

           NV0fNum      6.43E-1    ; sin(40)

           NV0fNum      7.66E-1    ; sin(50)

           NV0fNum      8.66E-1    ; sin(60)

           NV0fNum      9.40E-1    ; sin(70)

           NV0fNum      9.85E-1    ; sin(80)

           NV0fNum      1          ; sin(90)

Padding    NV0Space     XXXXX      ;Force new block

;Any NVEM that gets written during program execution gets placed below this point.

;That would typically be large parameter tables that can be changed by an end-user.


SL100: Onboard counter type A (OBCA)

The board incorporates an onboard counter type A (called OBCA). This is a high speed hardware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or wind speed using a rotating vane anemometer.

The OBCA exists on several SPLat boards. Generic documentation for the feature is provided in the Onboard counter type A guide.

The OBCA is connected to input number 7. There is some input filtering that limits the maximum count frequency. The maximum count frequency, assuming a squarewave input, depends on the supply voltage to the board. At 10V it is 7kHz, and it increases with supply voltage to 18kHz at 24V supply. The accuracy is approximately 0.1% (this varies from product to product, depending on whether they use a crystal for timing or not. This board has a crystal).


SL100: OnBoard Counter type B

The board incorporates an onboard counter type B (called OBCB). This is a medium speed Firmware counter that can be used for frequency or totalized counting. Typical applications include mains (line voltage) frequency measurement, engine RPM, belt speed (using a pulse encoder) or wind speed using a rotating vane anemometer.

The OBCB exists on several SPLat boards. Generic documentation for the feature is provided in the OnBoard Counter type B guide.

The OBCB is implemented on inputs 0 though 7. The maximum count frequency is approximately 1.8kHz, assuming a perfect square wave input. The accuracy is approximately 0.1% (this varies from product to product, depending on whether they use a crystal for timing or not. This board has a crystal).


SL100: OnBoard Quadrature Counter

The SL100 implements an OnBoard Quadrature Counter (OBQC) on inputs 0 and 1. A quadrature counter decodes the dual channel outputs of a quadrature encoder and maintains an up/down counter. A quadrature encoder is a (usually rotary) encoder that produces two output signal that indicate the amount of rotations and the direction of rotation.

You should make sure that the encoder cannot generate input state changes more often that 400uS apart. You should also make sure your program is designed to never perform any writes to non-volatile memory when it must track an encoder with the OBQC.

For more information see the generic OBQC documentation.


SL100: CPU device

The SL100 with late Firmware revisions can respond to UV instructions that address the CPU device. Some of the responses are dependent on the hardware configuration.

Board Type

The board will respond to the following board-type poll:

  SPxPoll2      3,!CPU

by placing its board type identification number on U0 and U1. The response is a function of the type (model) of board and the number of connected XPice expansion boards (e.g. XBIO16, XIRO16, HIO16). The numbers can be fetched from U0 and U1 by the PushU instruction.

The following table defines the responses for the SL100 with varying amounts of expansion:

Configuration | U0 | U1
SL100 | 0 | 16
SL100+16XPice | 0 | 17
SL100+32XPice | 0 | 18
SL100+48XPice | 0 | 19
SL100+64XPice | 0 | 20

Note that you can discover in this way how much expansion has been added but not the type of board.

Interpreting the number of XPice points can be a little tricky. For boards with segregated inputs and outputs, like XIRO16 and HIO16, each 8I/8O board corresponds to 16 XPice points. For the XBIO16, each bidirectional point corresponds to one input and one output (and appears as such on the SPLat/PC screen). That means one XBIO16 board gets counted as 32 XPice points although it only adds 16 I/O points to your configuration.

PWM base frequency (Dialect 20 or later)

The analog outputs on the board are generated by a pulse width modulated digital pulse train which is filtered (averaged) by an analog filter to produce a DC output. Normally the frequency of the PWM pulse train is 16kHz. The base frequency can however be altered using the instruction:

SPxCmd1          0,!CPU

with register U0 preset to a value between 0 and 7. The resulting PWM frequencies are given in the following table:

U0 | PWM frequency
0 | 16kHz (Default)
1 | 8kHz
2 | 4kHz
3 | 2kHz
4 | 1kHz
5 | 500Hz
6 | 250Hz
7 | 125Hz

Any other value will result in a fatal runtime error.

All analog output pins are affected. This feature is designed to be used with PWM output circuits such as DC motor speed controls. Be aware that a low PWM frequency will have an impact on any actual analog (filtered DC) outputs. Our filter circuits are normally designed for the default PWM frequency. Using a much lower frequency will produce ripple in the DC output.

When used with an AnOut instruction, the resolution is 8 bits. With fAnOut it is 10 bits.

Modify SuperTimer 10mS clock interval (Dialect 20 or later)

You can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.

CPU temperature readout (Dialect 21 through 27 or later (Deleted after 27))

An SPxPoll4 0,!CPU returns the floating point temperature of the CPU chip in °C at locations U(0) --> U(3)

An SPxPoll4 1,!CPU returns the floating point temperature of the CPU chip in °F at locations U(0) --> U(3)


SL100: Option links

SL99 option links

There are two option links on the SL100 board that can be read as inputs 8 and 9. You can use these in your program to select alternative operating modes or other end-user options. With a jumper inserted as illustrated (the red object), the corresponding input will be ON.


SL100: SPice connector

This section describes the specifics of the SPice connector on the SL100. The SPice expansion scheme in general is documented elsewhere in the SPLat Knowledge Base, under the SPLat Expansion Framework.

The SPice connector is the longer of the two single pin-row connectors located on the edge of the board. It is labeled "SPICE". It is identical to the SPice connector on the MMi99, except the pins map to different I/Os.

The SPice connector signals are as follows:

Pin | As input | As output | As AnIn | As AnOut | U(n) | Comments
1 | Raw positive board supply voltage
2 | +5V regulated. Permissible load 5mA
3 | Negative bias voltage. Permissible load 1mA.
4 | 12 | 8* | C (2) |  | 0 | Doubles as Tx for TSP (D<=19); Doubles as Xwire data (D>=20)
5 | 13 | 9* | D (3) |  | 1 | Doubles as Rx for TSP (D<=19)
6 | 14 | 10* | E (4) |  | 2 |
7 | 10* | 11 | F (5) |  | 3 |
8 | 11* | 12 |  | H (7) | 4 |
9 | 15 | 13 |  | G (6)* | 5 | Doubles as the programmable thermistor drive voltage.
10,11,12 | 0V (system ground)

The U(n) column refers to the position in U of the pin's configuration byte during a SPiceConfigU instruction.

Analog channel designators are show both in the old letter style and the newer (dialect 18 floating point analog I/O) numeric channel numbers.

The analog output function (pins 8 and 9) is actually a pulse width modulated (PWM) digital output. On a SPice board this can be filtered to yield a smooth DC voltage, giving an analog output.

The PWM signal on SPice pin 9 is also used for the thermistor drive voltage on the main board; naturally it can only be used for one thing at a time.

Pins 4 through 9 can all be set up for different functions with different SPice boards. The function of the pins is set in your program using the SPiceConfigU instruction. Default settings are marked *.

The 5mA permissible load on the +5V supply pin is a nominal allowance that has been included in the overall power supply budget with board supply voltages up to the maximum rating of 32V. This can be increased significantly if lower supply voltages are used.

We do not intend users to use the SPice connector other than for add-on boards supplied by us. The above information is provided mainly to make you aware of the possibilities, perhaps to inspire you to have a custom SPice board made.


SL100: Xwire

The SL100 with Firmware dialect 20 and later supports Xwire. Xwire is an easy to use method of connecting several boards together into a multiprocessor network.

The Xwire connections are on the SPice connector. The board's Xwire address is set via your program, if it is to be a slave.

Xwire documentation.

The maximum number of data bytes that can be transferred into or out of the SL100 via Xwire is 32.

Electrically the SL100 can drive 10 unit loads and represents one unit load. That means one SL100 board can drive 10 other SL100s or other "unit load" boards.

The master configuration table may contain a maximum of 50 entries. This is more than the hardware limit. Conceivably one slave board might have more than one logical device (address) on it.


SL100: Comms connector

The comms connector is a standard DB9. It provides an RS232 compatible interface, and is used for communications with a PC for programming and use with SPLatLink.

The RS232 interface is a basic interface with transmit data, receive data and ground. Pinouts are as follows:

Pin | Signal name | Comments
1 |  |
2 | RxD | Serial data output from board
3 | TxD | Serial data input to board
4 | DTR | Permanent +5V via 2.2K (output)
5 | Gnd | 0V, signal ground
6 |  |
7 | RTS | Request to Send signal out of board
8 |  |
9 |  |

The interface has a special design that allows several boards' interfaces to be connected in parallel, like pin to like pin. This makes possible small systems of up to 5 boards in a multi-drop communications configuration, connected perhaps to a shared modem. Please contact SPLat Controls for information about suitable Firmware to support this arrangement.

See also RS232 pinouts: A primer


SL100: Communications

The SL100 implements 3 serial communications protocols: Our proprietary SPLatLink, the industry standard ModBus RTU (master or slave), and the "User programmable protocol".

These protocols use the programming (serial) port. This is an RS232 compatible port with a 9-pin connector. This will connect directly to any PC that has an RS232 (COM port), or to any other device that has RS232, given the right cable. The maximum cable length is 5m. You can also use an MB485 adaptor to convert it to RS485, which will allow connections up to hundreds of meters.

User programmable protocol

This refers to the ability of SPLat to be programmed to send and received arbitrary characters. More information.

ModBus RTU Slave

ModBus is the oldest and most widely accepted protocol in automation. This controller supports ModBus RTU slave mode. In the initial implementations the ModBus feature allows message lengths up to the ModBus limit of 255 bytes. This may be reduced in the future as we add new features and have to "take back" some of the memory. In practice a 64-byte message can transfer 57 bytes of data.

More information on ModBus.

ModBus RTU Master

This controller supports ModBus RTU Master mode.

SPLatLink

SPLatLink is our proprietary protocol for communicating with a SPLat controller while it is running your SPLat Program.

SPLatLink is deprecated. We discourage its use in custom applications.


SL100: Programming

The SL100 is a member of the SPLat family of controllers. That means that it shares its programming language and programming tools with all other SPLat controllers. Whatever you learn on the SL100 is applicable to other models. The only differences will be those relating to particular hardware resources and features, and dialect differences. "Dialect" is a term we use to describe differences between versions of the SPLat language, which really means that we are continually adding new language features which are not available on older controller products.

You program the board by writing and debugging a program to suit your application in your PC. You then download the program to the board and complete debugging and overall system testing. Once the program has been downloaded to a SPLat board it will remain secure in the board. You can download a new program to the board at any time, in which case the old one is overwritten. You cannot upload a program from a board back into your computer, so it is impossible for a competitor to steal your program out of a board.

The PC software you use for programing is called SPLat/PC. SPLat/PC gives you a wide range of programming and debugging tools. It incorporates a help file with reference material and our classic, highly acclaimed, programming tutorial. You can download SPLat/PC free from here.

SPLat/PC also features a series of semi-interactive "mini-tutorials". These are designed to give you a bit of a kick start with SPLat. You should start your exploration there.

The proprietary SPLat programming language contains a repertoire of over 400 instructions. These instructions cover a huge range of functions. The instruction set differs from conventional programming languages and microprocessor assembler languages in that it has a strong emphasis on the needs of control applications. In particular, there is a subset of just 14 instructions called FastTrack. The FastTrack subset contains all you may ever need in order to implement simple timing and sequencing applications with batch counting and multiple interlocks.

In December 2005 we introduced MultiTrack programming. This is an extremely easy method of writing programs that can (seemingly) do several things at once. (For experts: MultiTrack is a non-preemptive multitasking scheduler that is extremely easy to learn and use). MultiTrack extends FastTrack, so that with just 16 instructions you can write useful multitasking programs. The mini-tutorials in SPLat/PC include an introduction to MultiTrack. It has been our experience over many years that a lot of people shy away from MultiTrack because multitasking is a large conceptual leap. The MultiTrack mini-tutorial is excellent in that regard, as it presents the concept in an extremely approachable way. Even if it takes you several hours to wrap your head around, invest the time! The investment will pay off handsomely in anything but the most trivial program. After you have grasped the basics of MultiTrack, your next port of call should be the more detailed MultiTrack tutorial.

The 400+ instructions that comprise the full SPLat programming language are documented within the SKB in the Programming Reference. Some of these are instructions we have introduced to simplify common control tasks even further, such as blinking an LED, and form an extension of the FastTrack concept. Therefore, it's a good idea to look there once you move on from pure FastTrack.

If you need even more knowledge you can work with the tutorial built into the SPLat/PC help file. It starts with a FastTrack tutorial that approaches FastTrack that is different from the approach taken in the online course (this is older material), then goes into the "classic" tutorial. The classic tutorial starts off with the nitty gritty details of the internal registers in SPLat. It then works up from there with a detailed expose, along with many examples, of the vast majority of SPLat instructions and how they are used.

You can also program this controller in C. Click here for details.

Previous Topic

Next Topic

In This Chapter

SL100: Programming cable


SL100: Programming cable

The controller needs to be connected to a PC for program download. The full process of connecting to the PC is described in the help file that comes with SPLat/PC, our programming software.

The connection cable you will need is a simple serial cable. You can buy these in most computer stores. If you make your own you need the following wiring (these pinouts are for DB9 connectors):

Controller | Computer
Pin 2 | Pin 2
Pin 3 | Pin 3
Pin 5 | Pin 5

You also need a computer with a serial (COM) port. Modern notebook computers tend not to have COM ports. In that case you will need a USB to Serial adaptor. Not all such adaptors work equally well. The ones we find work are based on the FTDI chipset, and are for sale on our website.


SL100: MultiTrack

The SL100 implements our MultiTrack multitasking programming system. This system makes it extremely easy to write programs that can do several things at once, for example monitor a limit switch at the same time as watching for an operator input. The capacity is 32 concurrent tasks.


SL100: Operating temperature ratings

The maximum allowable operating temperature of the board is governed by several factors.To determine the highest permissible operating temperature you need to evaluate each factor separately and then use the worst (lowest temperature) answer.

The factors that must be taken into account are:

  1. The semiconductors on the board have a maximum ambient temperature rating of 70°C.
  2. The 5V power supply regulator will shut down and self-protect if its junction temperature exceeds 150°C. How hot it actually gets depends heavily on the supply voltage. The table below will help you with this factor.
  3. The chip driving the outputs will also self-heat, and eventually fail if run too hot for too long. Evaluating this factor is discussed under SL100 External digital outputs
  4. Hot components fail faster than cool components. For semiconductors, lowering the temperature by 10°C doubles the life time.

5V regulator

The worst case load on the LM317 regulator is 55mA. With a maximum permissible operating junction temperature of 125°C and a junction to ambient thermal resistance of 50°C/W, this translates into the following maximum ambient temperatures versus supply voltage:

Supply V | Temperature
up to 25V | 70°C
28V | 60°C
32V | 50°C

Note: Figures at 28V and 32V are given for reference only. We do not recommend operating the board at voltages over 24V for sustained periods of time. The board can be damaged if the power supply voltage goes over 32V, even momentarily.


SL100: Firmware revisions and reFlashing

This chapter contains the two related topics of Firmware revisions/updates and reFlashing.

Previous Topic

Next Topic

In This Chapter

SL100: reFlashing

SL100: Firmware version history and updates

SL99: Firmware version history and updates


SL100: reFlashing

SL100 boards are reFlashable.

reFlash is the ability of a SPLat board to have new operating Firmware loaded into it. The operating firmware is the program that we load into the board, that gives it its SPLat "personality". This is quite different from the program you write to tailor it to your application, which we call SPLatWare.

reFlash is available in 2nd generation boards, starting with the MMi99 with firmware version 3.3 or later (reFlash was developed after the earlier MMi99 versions were released). Older designs, which do not have reFlash, are SP108, SL88, MMi88, AJ18, PMC3, CA20, OEM32 and OEM36.

There are three reasons why you would ever want to reFlash a SPLat board:

  1. Because we introduce new firmware features quite frequently, it has become impossible to ensure that all our (and our dealers') inventory is fully up to date. Therefore it is possible that a board you receive does not contain the "latest and greatest". Thanks to the miracle of reFlash technology you can still update your board to the latest version.
  2. We goofed, and there's a bug in the existing firmware. For example a SPLat instruction giving incorrect results.
  3. We devised a new feature (for example new instructions) and you'd like to upgrade your existing board(s). This makes most sense if you keep a board for development. There is little point in upgrading boards already shipped in your product, unless you are also upgrading the SPLatWare and you have used new language features.


SL100: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for SL100. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date

rfl file

Description

V3.27

23Aug10

sl100v3-27.rfl

  • Dialect 23 support
  • Various enhancements to Xwire
  • Various bugs fixed

V3.18

20Jan10

sl100v3-18.rfl

  • Dialect 21 support
  • Fast program download support. Works with SPLat/PC 10.21.1 and later
  • OBLCD instructions can be mapped to an external HMI via ModBus
  • CPU temperature readout

V3.16

23Mar09

sl100v3-16.rfl

  • MODBUS master bug in function 1 and 2 fixed.
  • Bootloader bug fixed which could prevent successful reflashing of a board if NVEM alters memory to something other than $FF after the end of the SPLat program.
  • Demo program feature removed to free up space and resources.
  • User program space reduced by 4096 bytes to allow for future code expansion.
  • User now has 22,016 bytes for program and NVEM storage (previously 26,112 bytes).
  • The FixtoU instruction now preserves W.

V3.14

17Jun08

sl100v3-14.rfl

  • Fixed Xwire bugs

V3.12

sl100v3-12.rfl

  • Dialect 20, adds Xwire, 10mS timebase adjustment
  • Discontinues TSP

V3.11

16Jul07

sl100v3-11.rfl

  • Dialect 19, adds OBCB
  • Fixed bug in STScale
  • Improved interrupt handling for expansion I/O to reduce processor loading

V3.0

N/A

Initial release, dialect 18


SL99: Firmware version history and updates

The following table lists the most significant aspects of the various Firmware versions for SL99. They are listed with the most recent first. The latest version is available as an rfl file on the SPLat website in the customer support area. We have only listed publicly released versions.

If you are connected to the Internet, click a hot-linked filename below to download it directly from our website.

If you are working off a resources CD, you will find some or all of the file(s) in the reFlash folder on the CD (depending on the age of the CD)

Version / date

rfl file

Description

V3.43

7Sep06

SL99v3-43.rfl

Dialect 18 upgrade: fAnIn, fAnOut, ClrInstCount. This is the final version.

V3.42

10Jan06

SL99v3-42.rfl

Dialect 16 upgrade, adding over 70 new instructions and a raft of new features, including:

  • MultiTrack
  • SuperTimers (10mS to 46 hours)
  • Quadrature counting
  • NVEM page 0

V3.41

09May04

SL99v3-41.rfl

Added Mode 4 (totalizing counter) to the OBCA

Fixed incorrect handling of indexing in fStore, fRecallW and fRecallQ

V3.4 06Feb04

SL99v3-4.rfl

Fixed bug in OutputM

V3.3

28Oct03

SL99v3-3.rfl

Added dialect 12 instructions: Semaphores, GetTick10, GetTick100, Suspend, Resume

Increased RAM to 192 bytes and subroutine stack to 6 levels.Extended scope of InputFM and OutputM

Implemented CPU device attributes:

Firmware revision

Dialect

Board type

Subroutine nesting depth

Processor reset flags

NOTE: You will have to install the V3.0 reFlash utility to use with this and later upgrades. The older V2.x will not work.

V3.2

25Nov02

SL99v3-2.rfl

Added dialect 11 instructions:

DecMRetIfZ, DecMRetIfNZ

V3.1

31Oct02

SL99v3-1.rfl

Added OnBoard Counter type A (OBCA)

V3.0

N/A

Initial release


MMi202 Product documentation

The MMi202 has been replaced by the MMi203. The documentation is consolidated under MMi203.


MMi201 Product documentation

The MMi201 will shortly be replaced by the MMi202. The documentation is consolidated under MMi202.


MMi200 product documentation

The MMi200 has been replaced by the MMi201, and then the MMi202. The documentation for all products is consolidated under MMi202.


MMi99 Product documentation

The MMi99 has been replaced by the MMi201. The documentation for both products is consolidated under MMi201.


SL99 product documentation

The SL99 has been replaced by the SL100. The documentation for both products is consolidated under SL100.


CC16 product documentation

The CC16 is documented along with the newer CC18. The two controllers are almost identical, with only two differences:

  • The CC16 does not have analog inputs.
  • The CC16 does not have a programmable bicolour status LED.

Please use the CC18 Product Documentation for the CC16. Just ignore all references to analog or the status LED.


Product documentation: Expansion boards

Expansion boards increase the I/O capacity of SPLat controllers.

Previous Topic

Next Topic

In This Chapter

MBIO16: 16 bidirectional I/O with high current MOSFET outputs

FP16: 8 inputs (NPN/PNP) and 8 outputs

XBIO16: 16 bidirectional I/Os

XIRO16: 8 inputs and 8 relay outputs (5A)

HIO16: 8 high power relays (20A/1HP) and 8 isolated inputs


MBIO16: 16 bidirectional I/O with high current MOSFET outputs

Previous Topic

Next Topic

In This Chapter

MBIO16: Introduction

MBIO16: Compatibility

MBIO16: Timing restrictions

MBIO16: Power requirements

MBIO16: Inputs and outputs

MBIO16: Connecting to a SPLat controller

MBIO16: Programming


MBIO16: Introduction

This document provides information on the use of the SPLat MBIO16 expansion board with up to 2ADC output switching capability per output.

The MBIO16 is an I/O expansion board that uses the XPice interface convention. It adds 16 bidirectional I/O points to any XPice capable SPLat controller. The board connects to its "host" SPLat controller board via a cable from the SPx expansion connector on the host. It also has an SPx output connector, allowing several MBIO16 (or other XPice expansion boards) to be daisychained.


MBIO16: Compatibility

There are several issues you should be aware of regarding compatibility of the MBIO16 with other SPLat products.

Controller boards

The MBIO16 works with 2nd generation SPLat controllers. At time of writing that means the MMi20x/MMi99 the SL99 and the MS12x, plus certain custom SPLat controllers.

That means the following older controllers cannot be used with the MBIO16: SP108, SL88, MMi88, OEM32, OEM36, AJ18 or any custom board designed before 2002. Please consult us regarding custom boards designed after January 2002.

Other expansion boards

You can use MBIO16 with other XPice boards, such as the HIO16.

Note that this does not preclude Touch of SPice boards. They use a different connector, and do not involve the high speed serial SPx bus.

Program timing

There are some things you need to be aware of concerning the timing of inputs and outputs with the MBIO16. This is documented under Timing Restrictions.


MBIO16: Timing restrictions

The MBIO16 connects to the main controller board via a high speed serial data bus and a protocol called XPice, which is implemented on 2nd generation SPLat controllers (see SPLat Expansion Framework). That means that input changes and output changes are updated only when the controller reads and writes bus updates.

2nd generation controllers at time of writing are the MMi20x/MMi99 and the SL99. These perform XPice updates every millisecond. That means the effect of any programmed output change (ON, OFF, Output etc) will not appear on the output pin for up to 1mS after the instruction is executed.

Similarly, any change on an input pin will not be seen by your program for up to 1mS after it happens. That has very little practical significance unless you are using InputF instructions (all other input type instructions have contact debounce that introduces a delay of up to 20mS).

Because each I/O point on the MBIO16 is in fact an input and an output connected together, you will see the effect of this delay if you try and turn on an output then immediately read the equivalent input with an InputF or InputFM. In all probability the input will not appear as on (this would only be detectable in a program actually running in the board).

Similarly, you would not be able to use an MBIO16 output to generate a high speed serial bit stream. For example, there are LED displays available that are driven by a serial bit stream consisting of clock and data. We have successfully used these on SPLat boards, but it would be difficult to get it to work off an MBIO16 (you would have to insert delays, which is clumsy and awkward).


MBIO16: Power requirements

Because the MBIO16 can potentially carry quite large currents (up to 32A peak) it is important to take special care with the power supply wiring.

The MBIO16 board may be powered off the same power supply as the SPLat controller board it serves, or off a different supply. However, it is very wise to run separate power wires for controller and expansion boards. The MBIO16 requires a supply voltage of 10-28VDC, with an absolute maximum rating of 32VDC. It typically draws less than 20mA plus whatever current is flowing through the I/O pins, plus about 5mA per illuminated LED (at 24V, proportionally less at lower voltages).

Note: If you use two power supplies, the expansion board(s) must be powered up at the same time as, or before, the controller.

It is absolutely essential that none of the current from the external loads or inputs flows through the cable back to the controller. For this reason 0V on the MBIO16 must be wired independently back to the main power supply. If you use separate power supplies you must tie their negative (0V) terminals together and to the 0V terminals of the controller and MBIO16. If you fail to do this you will at best experience erratic performance and at worst damage either the MBIO16, the controller board or both.

You can supply power for the board itself via the quick connect tabs labeled 0V and +V and located at either end of the row of LEDs. Note that the + pins on the I/O connectors are joined together on the board and connected to the +V tab, similarly for the 0V pins. It is a good idea to separately wire all 0V pins back to the power supply common. You can use the +V tab to power the board, and then use the + pins on the I/O connectors to feed power to the loads.

Wiring arrangements

The following diagrams show some different possible connection schemes for output loads and power, with emphasis on the power supply feed. Which scheme you use will depend on what is most convenient for wiring and on the load current(s) you are running. In these diagrams the loads are represented by generalized rectangles. Finer detail for various load types is provided elsewhere. Just remember that the load (relay coil or whatever) goes between the board's I/O pin and the positive supply.

In the first diagram, above, the power is fed to the MBIO16 via the quick connect tabs labeled V+ and 0V (thick lines). The loads get their positive connections off the V+ pins on the I/O connectors (which are fed from the V+ quick connect tab via the board). The 0V return is taken off the 0V quick connect tab. The pink arrows indicate the path the load currents will follow. The total load current flows through the quick connects.

In the next diagram, above, the V+ common for the loads is run external to the board. A wire is still run to the V+ quick connect tab, to provide power for the board itself. That wire will only carry low current. The total load current flows out of the 0V quick connect tab.

In the final diagram we have run 0V return wires from the 0V pins on the two I/O connectors, as well as out of the 0V quick connect tab. This provides a lower resistance, redundant path. How the current divides between the 3 possible paths depends on wire and contact resistances. In practical terms it is impossible to predict the split, but having multiple paths is definitely advantageous.

Why would you use separate power supplies?

The output circuits on the MBIO16 contain clamp diodes connected to the + supply pin. These are there to prevent damage when switching inductive loads like relay coils. Under Connecting output devices you will find a diagram of how an inductive load is connected (load B, with a parallel diode). The diode on that diagram is replicated on the board (we still recommend an external diode to reduce switching noise). The diode must be connected to the same + supply as the load. For that reason, the MBIO16 + supply pin must be connected to the same power supply as the load (even if you are driving non-inductive loads).

If, for any reason, you need to power the controller off a voltage other than that driving the loads on the MBIO16, you can use two different power supplies providing you observe the above rules.

Wire gauges

The currents that can potentially flow in and out of an MBIO16 board are high - high enough to over-heat connecting wires. In order to avoid a fire hazard, all wires connected to the MBIO16 should be rated at equal to or greater than the current limit or fusing level of the power supply. It is your responsibility to comply with all relevant wiring rules in your locality. Disclaimer.


MBIO16: Inputs and outputs

The picture above shows the locations of the connectors on the MBIO16 board. These are:

I/O points 0-7 and 8-15

These two 10-pin connectors provide the connections to your input and output devices.

Control connection in (SPx IN)

This 8-pin connector is the control input from the SPLat controller (host) board. It must be connected via a suitable cable assembly to the SPx connector on the host.

Control connection out (SPx OUT)

This 8-pin connector can be connected to another SPx board's control input (SPx IN) connector. In this way several boards can be daisychained together.

+V and 0V quick connects

Power is supplied to the board via these terminals. More information

Previous Topic

Next Topic

In This Chapter

Some common terminology

MBIO16: Where to get matching connectors

MBIO16: I/O connections


MBIO16: Where to get matching connectors

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.


MBIO16: I/O connections

Each of the 16 I/O pins on the MBIO16 is bidirectional. That means each one can be used as an input or as an output. In actual fact, each pin is connected onboard to an input circuit and to an output circuit, in parallel. This works because when an output is OFF, it does nothing. Only when an output is turned on does it become a low resistance path to 0V. Hence, if the output circuit is left OFF, the input can respond to an external input device, say a switch or a sensor.

There is a single shared LED indicator per I/O pin. One end of the LED is connected to the + (positive) supply via a resistor, and the other end is connected to the I/O pin. Hence, when the I/O pin is connected to 0V the LED lights up. This can be due either to the output being switched on by your program, or by an external input device connecting the pin to 0V.

The input sensing circuit represents a large resistance value to 0V (some 100's of kOhms). A very small current will flow from the LED circuit through this resistance when the I/O pin is OFF. That means that when the LED should be off it will glow dimly. This is perfectly normal and does not mean there is anything wrong.

In This Section

MBIO16: Connecting input devices

MBIO16: Connecting output devices


MBIO16: Connecting input devices

The picture above shows the two I/O connectors and two typical input devices.

Each connector contains the connections for 8 I/Os.

In the picture A is the output of an NPN style sensor using I/O 10 as its input. B is a simple switch connected to I/O 4. The sensor would also require a positive supply voltage to power it. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

The input is ON when the input pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - (V(supply) - 1.6V) / 4700

The leading minus sign simply means the current is flowing out of the pin. The 1.6V represents the voltage drop of the input indicator LED. The 4700 represents the input pullup resistance.

This equation translates to about 2.2mA with a 12V supply and 4.8mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognize the input as ON when the voltage on the input pin is typically less than 6V. That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input is related to the threshold of the input chip, which has fairly wide uncertainty. Due to this uncertainty the input can only be guaranteed to be ON if it is less than 3V, and can only be guaranteed to be seen as OFF if it is over 9V. The range between 3V and 9V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

How to blow up a bidirectional I/O circuit

You can blow up the I/O circuits by:

  1. Exceeding output current or voltage ratings.
  2. Connecting the I/O pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than 4-500 mA) and turning (programming) the output ON
  3. Connecting the output pin to any negative voltage.
  4. Connecting the output pin to any voltage greater than the positive supply.
  5. Switching an output load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  6. Driving (output) an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.

Driving (output) an incandescent lamp load rated at more than 10% of the output's peak output rating. Incandescent lamps draw a warm-up surge current of many (up to 15) times their rated steady state current.


MBIO16: Connecting output devices

The above picture shows the two I/O connectors and three typical output devices.

Each connector contains the connections for 8 I/Os. It also has a positive supply connection labeled + and a common return labeled 0V.

In the picture A is an LED with a series current limiting resistor (plain LEDs must always have something in series to limit the current!).

B is a relay coil or solenoid. It has a diode across it to prevent inductive kick-backs from the coil. Although the board has suppression diodes onboard, we recommend external diodes to reduce noise problems. The diode can be a 1N4004 or similar 1A rectifier diode.

C is a solid state relay. Note the polarity labels.

Click here for more information on power supply distribution.

The (digital) outputs are implemented using power MOSFETs. The onboard indicator LEDs are connected directly to the output pin, and returned to the positive supply via (typically) 10K current limiting resistors.

The outputs consist of MOSFET switches between the output pin and 0V. When the output MOSFET is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage. If you set the multimeter to 200mADC, the output LED will turn on and you will get a reading of approximately V/10 milliamps, where V is the supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output device will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built onto the board. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the coil as close to it as as possible, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

The following ratings are provisional

The maximum allowable output current is 1.5A steady state, with brief (5S) peaks of 2A allowed. There is however a limit to how much total current the board can handle. This relates to the internal heating of the MOSFETS and the fact they are packed close enough to heat each other up, and to the current ratings of the connectors.

Overall you should stay within the following boundaries:

  • Ambient temperature should be no more than 60°C
  • The +V and 0V pins on the white I/O connectors are rated at 7A.
  • The +V and 0V quick connect terminals and associated printed circuit traces are rated at 25A.
  • You can have all outputs on continuously at up to 1A per output.
  • You can have every other output on continuously at up to 1.5A, with the others turned off (or used as inputs)
  • You can turn every fourth output on for 60 seconds at up to 2A, with the others turned off (or used as inputs)

In a typical application, driving say hydraulic solenoid valves, it would be unusual for more than a few outputs to be active at any one time.

There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better.

When semiconductor switches fail they are more likely to become stuck ON than OFF.


MBIO16: Connecting to a SPLat controller

The MBIO16 needs to be connected to a SPLat host controller. The connection is made from the SPx connector of the controller to the SPx IN connector on the MBIO16 board. You can cascade several XPice boards by daisychaining SPxOut on one board to SPxIn on the next.

A suitable cable for making this connection is the SPXC60, a 60cm long cable assembly supplied by SPLat Controls. If you buy the MBIO16DK (Developer's Kit), the cable is supplied as part of the kit. Alternatively you can make your own.

You also need to supply power. Normally the board will run off the same power supply as the controller (and any other XPice boards). We recommend running separate wires back to the power supply for each board. This will help maintain a "star point" ground connection, which is a good strategy for reducing noise problems.


MBIO16: Programming

Programming the MBIO16 is very simple. All standard range controllers released subsequent to the MMi99 (after May 2002) will support the board.

SPLat/PC can be configured to handle the MBIO16 board. The MBIO16 is represented as an extra 16 inputs and an extra 16 outputs. Because each I/O pin on the MBIO16 can be either an input or an output, SPLat/PC shows them as both. You must yourself keep track of which mode you are using each pin in.

Once SPLat/PC is configured for the MBIO16, you program it as you would any other inputs or outputs. There are some minor timing restrictions.

There are two ways you can configure SPLat/PC to recognize the MBIO16 board:

1. Automatic configuration

If you connect the MBIO16 to a compatible SPLat controller, and then connect the controller to SPLat/PC, if SPLat/PC recognizes the controller and MBIO16 combination (i.e. you have a recent enough SPLat/PC), SPLat/PC will automatically configure itself. You will see the I/O window grow to accommodate the extra I/O from the MBIO16.

2. Manual configuration

Within SPLat/PC, there is a menu item to configure SPLat/PC to the board you are working with. This menu item is File>Configure>Board Type. Each controller board that can support XPice expansion boards like this one is represented by several variants. These variants relate to the amount of XPice expansion capacity connected to the controller.

For example, for the MMi201 there are the following variants:

MMi201 | Just the controller, no expansion
MMi201+16XPice | Controller plus 16 XPice I/Os
MMi201+32XPice | Controller plus 32 XPice I/Os
MMi201+48XPice | Controller plus 48 XPice I/Os
MMi201+64XPice | Controller plus 64 XPice I/Os

Because the XBIO has, in effect, 16 inputs and 16 outputs, you need to select MMi201+32XPice (assuming you have just one MBIO16 connected).

Input and output numbers

You will need to carefully work out what input and output numbers apply to each MBIO16 I/O point. This will depend on what type of controller the MBIO16 is connected to, and on what other MBIO16 (or other XPice boards) are involved.

For an MBIO16 connected directly to a controller, the input numbers 'run on' directly after the controller's onboard inputs, and the output numbers 'run on' directly after the controller's outputs. On an MBIO16 connected to an MMi201, which has different quantities of inputs and outputs, this means that a given MBIO16 I/O point will have a different number seen as an input and seen as an output.

For an MBIO16 that is connected to another MBIO16, the input and output numbers 'run on' from the XBIO closer to the controller.

The easy way to get this clarified is to actually hook the boards up, connect to SPLat/PC and experimentally activate inputs and outputs. The SPLat/PC I/O window will show what's happening.

Implementing systems with more than 64XPice I/Os

The limit on I/O count implied above is not absolute. Using the SPiceX method you can go considerably further. Tutorial.


FP16: 8 inputs (NPN/PNP) and 8 outputs

Previous Topic

Next Topic

In This Chapter

FP16: Introduction

FP16: Compatibility

FP16: Timing restrictions

FP16: Power requirements

FP16: Inputs and outputs

FP16: Connecting to a SPLat controller

FP16: Programming


FP16: Introduction

This document provides information on the use of the SPLat FP16 expansion board.

The FP16 is an I/O expansion board that uses the XPice interface convention. It adds 8 digital inputs and 8 digital outputs to any XPice capable SPLat controller. The board connects to its "host" SPLat controller board via a cable from the SPx expansion connector on the host. It also has an SPx output connector, allowing several FP16 (or other XPice expansion boards) to be daisychained.


FP16: Compatibility

There are several issues you should be aware of regarding compatibility of the FP16 with other SPLat products.

Controller boards

The FP16 works with 2nd generation SPLat controllers. At time of writing that means the MMi20x/MMi99 the SL99 and the MS12x, plus certain custom SPLat controllers.

That means the following older controllers cannot be used with the FP16: SP108, SL88, MMi88, OEM32, OEM36, AJ18 or any custom board designed before 2002. Please consult us regarding custom boards designed after January 2002.

Other expansion boards

You can use FP16 with other XPice boards, such as the HIO16.

Note that this does not preclude Touch of SPice boards. They use a different connector, and do not involve the high speed serial SPx bus.

Program timing

There are some things you need to be aware of concerning the timing of inputs and outputs with the FP16. This is documented under Timing Restrictions.


FP16: Timing restrictions

The FP16 connects to the main controller board via a the high speed serial data bus and a protocol called XPice, which is implemented on 2nd generation SPLat controllers (see SPLat Expansion Framework). That means that input changes and output changes are updated only when the controller reads and writes bus updates.

2nd generation controllers at time of writing are the MMi200/MMi99, MS12 and the SL99. These perform XPice updates every millisecond. That means the effect of any programmed output change (ON, OFF, Output etc) will not appear on the output pin for up to 1mS after the instruction is executed.

Similarly, any change on an input pin will not be seen by your program for up to 1mS after it happens. That has very little practical significance unless you are using InputF instructions (all other input type instructions have contact debounce that introduces a delay of up to 20mS).

You will see the effect of this delay if you connect an output to an input then try and turn on the output and immediately read the equivalent input with an InputF or InputFM. In all probability the input will not appear as on (this would only be detectable in a program actually running in the board).

Similarly, you would not be able to use an FP16 output to generate a high speed serial bit stream. For example, there are LED displays available that are driven by a serial bit stream consisting of clock and data. We have successfully used these on SPLat boards, but it would be difficult to get it to work off an FP16 (you would have to insert delays, which is clumsy and awkward).


FP16: Power requirements

The FP16 board may be powered off the same power supply as the SPLat controller board it serves or off a different supply. The FP16 requires a supply voltage of 10-28VDC, with an absolute maximum rating of 32VDC. It typically draws less than 20mA plus whatever current is flowing through the I/O pins (about 5mA per illuminated LED at 24V, proportionally less at lower voltages).

Note: If you use two power supplies, the expansion board(s) must be powered up at the same time as, or before, the controller.

It is absolutely essential that none of the current from the external loads or inputs flows through the cable back to the controller. For this reason the 0V pin on each FP16 I/O connector must be wired independently back to the main power supply. If you use separate power supplies you must tie their negative (0V) terminals together and to the 0V terminals of the controller and FP16. If you fail to do this you will at best experience erratic performance and at worst damage either the FP16, the controller board or both.

You can supply power for the board itself via the 2-pin power connector. Note that the + pins on the power and I/O connectors are joined together on the board, as are the 0V pins. It is a good idea to separately wire all 0V pins back to the power supply common. You can use the + pin on the power connector to power the board, and then use the + pins on the I/O connectors to feed power to the loads.

Why would you use separate power supplies?

The output circuits on the FP16 contain clamps diodes connected to the + supply pin. These are there to prevent damage when switching inductive loads like relay coils. Under Connecting output devices you will find a diagram of how an inductive load is connected (load B, with a parallel diode). The diode on that diagram is replicated on the board (we still recommend an external diode to reduce switching noise). The diode must be connected to the same + supply as the load. For that reason, the FP16 + supply pin must be connected to the same power supply as the load (even if you are driving non-inductive loads).

If, for any reason, you need to power the controller off a voltage other than that driving the loads on the FP16, you can use two different power supplies providing you observe the above rules.


FP16: Inputs and outputs

The picture above shows the locations of the 5 connectors on the FP16 board. These are:

Inputs

This 10-pin connector provides the connections for your input devices.

Outputs

This 10-pin connector provides the connections for your output devices.

Control connection in (SPx IN)

This 8-pin connector is the control input from the SPLat controller (host) board. It must be connected via a suitable cable assembly to the SPx connector on the host.

Control connection out (SPx OUT)

This 8-pin connector can be connected to another SPx board's control input (SPx IN) connector. In this way several boards can be daisychained together.

PWR

Power is supplied to the board via this connector.

Previous Topic

Next Topic

In This Section

FP16: Some common terminology

FP16: Where to get matching connectors

FP16: Connecting input devices

FP16: Connecting output devices


FP16: Some common terminology

If your background is not in the electronics field, we may confuse you a bit with the way we refer to power supply pins, grounds, "common" terminals etc. In electronics we generally quote all voltages in relation to an assumed common or reference terminal. That common terminal is often called "ground", "gnd" or "circuit ground". It may or may not actually be tied to a physical ground point, for instance mains power supply earth.

In SPLat boards we usually label the "ground" terminal "0V". On boards that are powered off an external DC power supply (which is most off-the-shelf boards), 0V is the terminal connected to the negative terminal of the power supply. Measured relative to that pin, therefore, the positive power supply pin will be 24V (or 12V or whatever the supply voltage is) positive. We then say "we have a +12V supply". All boards contain a 5V regulator chip to feed the microprocessor and/or other logic chips. Its output voltage is said to be +5V, again measured relative to the 0V terminal.

Similarly, all input and output voltages are also measured relative to the 0V terminal, and will in virtually all cases be positive voltages. For example, on most SPLat models the input voltage threshold for digital inputs, and analog input and output voltages, are measured relative to 0V.


FP16: Where to get matching connectors

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.


FP16: Connecting input devices

The FP16 can be set for use in NPN or PNP input modes. "NPN" and "PNP" refer to two ways of electronically switching an on/off signal out of a sensor circuit. Many electronic sensors, like proximity switches, are available in both types or able to be set for one mode or the other. Think of NPN as "Negative switching" and PNP as "Positive switching". We prefer to talk about 0V or common rather than negative - see "Common terminology".

The above picture shows the input connector being used with NPN (left) and PNP (right) input connections. Note that the jumper circled in yellow must be set to the desired mode. It sets all 8 inputs at once.

NPN inputs

The picture shows an NPN type sensor 'A' with its output connected to the board's input 5. When the sensor is off, its output is an open circuit. When it turns on its output pin gets internally connected (through an NPN transistor) to its negative supply pin (our 0V or "common" line).

'B' is an ordinary switch, say a mechanical limit switch connected to input 3. In NPN mode it must be connected between the input pin and the 0V pin.

In NPN mode the input indicator LEDs will light up red when the input is on.

PNP inputs

The picture shows an PNP type sensor 'C' with its output connected to the board's input 2. When the sensor is off, its output is an open circuit. When it turns on its output pin gets internally connected (through a PNP transistor) to its positive supply pin (which is also our + supply line).

'D' is an ordinary switch, say a mechanical limit switch connected to input 5. In PNP mode it must be connected between the input pin and the + supply pin.

In PNP mode the input indicator LEDs will light up green when the input is on.

In PNP mode your SPLat program will see the inputs as inverted. An ON input gets read as OFF, and an OFF input gets read as ON. Your program must make a suitable adjustment.

Input currents

A current will flow through the board's input pin in the ON state. The magnitude of the current depends on the supply voltage. Assuming a dead short between the input pin and 0V (NPN mode) or + (PNP mode), the current is given by the equation

I(in) = (V(supply) - 1.8V) / 4700

The 1.8V represents the voltage drop of the input indicator LED. The 4700 represents the input pullup (NPN) or pulldown (PNP) resistance.

This equation translates to about 2.1mA with a 12V supply and 4.7mA with a 24V supply. This is low enough to be compatible with off-the-shelf sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching, which invariably means gold plated. There is an article about this here.

Voltage threshold

The input threshold is the voltage at the input pin where the board switches between on and off conditions. Typically this is 6V for the FP16. In practice the threshold voltage of the input is related to the threshold of the input chip, which has fairly wide uncertainty. Due to this uncertainty the input threshold can be anywhere between 3V and 9V. The range between 3V and 9V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

Note that the threshold voltage varies from product to product. Please check the specific documentation on the board(s) you are using.


FP16: Connecting output devices

The above picture shows the output connector and three typical output devices.

The connector contains the connections for 8 outputs. It also has a positive supply connection labeled + and a common return labeled 0V.

In the picture A is an LED with a series current limiting resistor (plain LEDs must always have something in series to limit the current!).

B is a relay coil or solenoid. It has a diode across it to prevent inductive kick-backs from the coil. Although the board has suppression diodes onboard, we recommend external diodes to reduce noise problems. The diode can be a 1N4004 or similar 1A rectifier diode.

C is a solid state relay. Note the polarity labels.

The red arrow lines show the positive current path from the power supply, through the coil load and into the I/O pin, then out of the board and back to the power supply common terminal. It is absolutely essential that you provide the wire from the 0V terminal of the appropriate I/O connector back to the supply. Without that wire the current will try and find a path via the control cable back to the control board, and in the process probably burn out something.

Technical explanation: To prevent heavy switching currents from the load flowing back through the controller and causing noise problem, we have isolated the XBIO16 output ground from the controller ground with a resistor.

The (digital) outputs are implemented using the ULN2803A, a chip that contains Darlington transistors. The onboard indicator LEDs are connected directly to the output pin, and returned to the positive supply via (typically) 10K current limiting resistors.

The outputs consist of transistor switches between the output pin and 0V. When the output transistor is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage. If you set the multimeter to 200mADC, the output LED will turn on and you will get a reading of approximately V/10 milliamps, where V is the supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output chip will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built into the chip. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the relay coil as close as possible to the relay, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

If you connect the loads (for example relays) separately to the power supply or to a separate power supply, it may be possible to remove the controller's supply connection and still have power fed to the controller via the loads and the onboard protection diodes. This can produce confusing or possibly even dangerous results if the controller gets enough of this "sneak" power to run. It is safer to use the convenience + supply pin on the connector to provide positive voltage to the load.

The maximum allowable output current is 400mA steady state, with brief (100mS) peaks of 500mA allowed. There is also a limit to how much total current one ULN2803A chip can handle. This relates to the internal heating of the chip.

Let's look at 2 extreme cases:

  1. All 8 outputs on one chip conducting the same current
  2. The allowable current concentrated in a few outputs.

Equal currents

The following table lists allowable current versus ambient temperature, when all 8 outputs are conducting the same current at once:

Temperature | Allowable current per output
25°C | 180mA
40°C | 160mA
50°C | 145mA
60°C | 130mA
70°C | 115mA

Concentrated currents

The following table shows the maximum allowable currents, when concentrated in a few outputs, versus temperature

Temp | 1st O/P | 2nd O/P | 3rd O/P | 4th O/P
25°C | 400mA | 400mA | 300mA | 0
40°C | 400mA | 400mA | 150mA | 0
50°C | 400mA | 400mA | 0 | 0
60°C | 400mA | 325mA | 0 | 0
70°C | 400mA | 220mA | 0 | 0

Naturally there are many other possible combinations, but the above should serve as a guide.

The above figures are intended as a guide only. In any event, the absolute maximum operating temperature for the ULN2803A chip is 70°C.

If you feel like doing the actual sums, here's the procedure.

  1. For each output, calculate its output voltage drop from the formula
    V(out) = 0.92 + 1.85 * I(out) [ I(out) in Amperes ]
  2. For each output calculate the dissipation in watts from the formula
    P=V(out) * I(out)
  3. Add up all the dissipation figures to give P(tot) in watts
  4. Calculate the maximum allowable ambient temperature from the formula
    Ta(max) = 125 - P(tot) * 55
  5. If you get an answer over 70°C then use 70°C

This calculation aims at a maximum internal (junction) temperature of 125°C, which is the maximum rating of the chip. There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better. The ULN2803A data sheet contains additional rating curves.

When semiconductor switches fail they are more likely to become stuck ON than OFF.


FP16: Connecting to a SPLat controller

The FP16 needs to be connected to a SPLat host controller. The connection is made from the SPx connector of the controller to the SPx IN connector on the FP16 board. You can cascade several XPice boards by daisychaining SPxOut on one board to SPxIn on the next.

A suitable cable for making this connection is the SPXC60, a 60cm long cable assembly supplied by SPLat Controls. If you buy the FP16DK (Developer's Kit), the cable is supplied as part of the kit. Alternatively you can make your own.

You also need to supply power. Normally the board will run off the same power supply as the controller (and any other XPice boards). We recommend running separate wires back to the power supply for each board. This will help maintain a "star point" ground connection, which is a good strategy for reducing noise problems.


FP16: Programming

All standard range controllers released subsequent to the MMi99 (such as SL99, MMi200 and MS12) will support the FP16, and the DK versions of those controllers ship with appropriate support software.

SPLat/PC can be configured to handle the FP16 board. The FP16 is represented as an extra 8 inputs and an extra 8 outputs.

Once SPLat/PC is configured for the FP16, you program it as you would any other inputs or outputs. There are some minor timing restrictions.

NPN versus PNP input handling

The FP16 allows you to select NPN or PNP configuration of the input circuits. It does not however have any fancy stuff to "correct" the logical polarity of what your program sees. The result is that in PNP mode the inputs seen by the SPLat program will be inverted. An ON input gets read as OFF, and visa versa. You must make appropriate adjustments in your program.

Configuring SPLat/PC

There are two ways you can configure SPLat/PC to recognise the FP16 board:

1. Automatic configuration

If you connect the FP16 to a compatible SPLat controller, and then connect the controller to SPLat/PC, if SPLat/PC recognises the controller and FP16 combination (i.e. you have a recent enough SPLat/PC), SPLat/PC will automatically configure itself. You will see the I/O window grow to accommodate the extra I/O from the FP16.

2. Manual configuration

Within SPLat/PC, there is a menu item to configure SPLat/PC to the board you are working with. This menu item is File>Configure>Board Type. Each controller board that can support XPice expansion boards like this one is represented by several variants. These variants relate to the amount of XPice expansion capacity connected to the controller.

For example, for the MMi200 there are the following variants:

MMi200 | Just the controller, no expansion
MMi200+16XPice | Controller plus 16 XPice I/Os
MMi200+32XPice | Controller plus 32 XPice I/Os
MMi200+48XPice | Controller plus 48 XPice I/Os
MMi200+64XPice | Controller plus 64 XPice I/Os

Because the FP16 has 8 inputs and 8 outputs, for a total of 16 I/O points, you need to select MMi200+16XPice (assuming you have just one FP16 connected). Similar for other controllers (like SL99).

Input and output numbers

You will need to carefully work out what input and output numbers apply to each FP16 I/O point. This will depend on what type of controller the FP16 is connected to, and on what other FP16 (or other XPice boards) are involved.

For an FP16 connected directly to a controller, the input numbers 'run on' directly after the controller's onboard inputs, and the output numbers 'run on' directly after the controller's outputs. On an FP16 connected to an MMi200, which has different quantities of inputs and outputs, this means that the 8 inputs and 8 outputs will not have identical numbers.

For an FP16 that is connected to another FP16, the input and output numbers 'run on' from the FP16 closer to the controller.

The easy way to get this clarified is to actually hook the boards up, connect to SPLat/PC and experimentally activate inputs and outputs. The SPLat/PC I/O window will show what's happening.

Implementing systems with more than 64XPice I/Os

The limit on I/O count implied above is not absolute. Using the SPiceX method you can go considerably further. Tutorial.


XBIO16: 16 bidirectional I/Os

Previous Topic

Next Topic

In This Chapter

XBIO16: Introduction

XBIO16: Compatibility

XBIO16: Timing restrictions

XBIO16: Power requirements

XBIO16: Inputs and outputs

XBIO16: Connecting to a SPLat controller

XBIO16: Programming


XBIO16: Introduction

This document provides information on the use of the SPLat XBIO16 expansion board.

The XBIO16 is an I/O expansion board that uses the XPice interface convention. It adds 16 bidirectional I/O points to any XPice capable SPLat controller. The board connects to its "host" SPLat controller board via a cable from the SPx expansion connector on the host. It also has an SPx output connector, allowing several XBIO16 (or other XPice expansion boards) to be daisychained.


XBIO16: Compatibility

There are several issues you should be aware of regarding compatibility of the XBIO16 with other SPLat products.

Controller boards

The XBIO16 works with 2nd generation SPLat controllers. At time of writing that means the MMi20x/MMi99 the SL99 and the MS12x, plus certain custom SPLat controllers.

That means the following older controllers cannot be used with the XBIO16: SP108, SL88, MMi88, OEM32, OEM36, AJ18 or any custom board designed before 2002. Please consult us regarding custom boards designed after January 2002.

Other expansion boards

You can use XBIO16 with other XPice boards, such as the HIO16.

Note that this does not preclude Touch of SPice boards. They use a different connector, and do not involve the high speed serial SPx bus.

Program timing

There are some things you need to be aware of concerning the timing of inputs and outputs with the XBIO16. This is documented under Timing Restrictions.


XBIO16: Timing restrictions

The XBIO16 connects to the main controller board via the high speed serial data bus and a protocol called XPice, which is implemented on 2nd generation SPLat controllers (see SPLat Expansion Framework). That means that input changes and output changes are updated only when the controller reads and writes bus updates.

2nd generation controllers at time of writing are the MMi20x/MMi99 and the SL99. These perform XPice updates every millisecond. That means the effect of any programmed output change (ON, OFF, Output etc) will not appear on the output pin for up to 1mS after the instruction is executed.

Similarly, any change on an input pin will not be seen by your program for up to 1mS after it happens. That has very little practical significance unless you are using InputF instructions (all other input type instructions have contact debounce that introduces a delay of up to 20mS).

Because each I/O point on the XBIO16 is in fact an input and an output connected together, you will see the effect of this delay if you try and turn on an output then immediately read the equivalent input with an InputF or InputFM. In all probability the input will not appear as on (this would only be detectable in a program actually running in the board).

Similarly, you would not be able to use an XBIO16 output to generate a high speed serial bit stream. For example, there are LED displays available that are driven by a serial bit stream consisting of clock and data. We have successfully used these on SPLat boards, but it would be difficult to get it to work off an XBIO16 (you would have to insert delays, which is clumsy and awkward).


XBIO16: Power requirements

The XBIO16 board may be powered off the same power supply as the SPLat controller board it serves or off a different supply. The XBIO16 requires a supply voltage of 10-28VDC, with an absolute maximum rating of 32VDC. It typically draws less than 20mA plus whatever current is flowing through the I/O pins (about 5mA per illuminated LED at 24V, proportionally less at lower voltages).

Note: If you use two power supplies, the expansion board(s) must be powered up at the same time as, or before, the controller.

It is absolutely essential that none of the current from the external loads or inputs flows through the cable back to the controller. For this reason the 0V pin on each XBIO16 I/O connector must be wired independently back to the main power supply. If you use separate power supplies you must tie their negative (0V) terminals together and to the 0V terminals of the controller and XBIO16. If you fail to do this you will at best experience erratic performance and at worst damage either the XBIO16, the controller board or both.

You can supply power for the board itself via the 2-pin power connector. Note that the + pins on the power and I/O connectors are joined together on the board, as are the 0V pins. It is a good idea to separately wire all 0V pins back to the power supply common. You can use the + pin on the power connector to power the board, and then use the + pins on the I/O connectors to feed power to the loads.

Why would you use separate power supplies?

The output circuits on the XBIO16 contain clamps diodes connected to the + supply pin. These are there to prevent damage when switching inductive loads like relay coils. Under Connecting output devices you will find a diagram of how an inductive load is connected (load B, with a parallel diode). The diode on that diagram is replicated on the board (we still recommend an external diode to reduce switching noise). The diode must be connected to the same + supply as the load. For that reason, the XBIO16 + supply pin must be connected to the same power supply as the load (even if you are driving non-inductive loads).

If, for any reason, you need to power the controller off a voltage other than that driving the loads on the XBIO16, you can use two different power supplies providing you observe the above rules.


XBIO16: Inputs and outputs

XBIO full board

The picture above shows the locations of the 5 connectors on the XBIO16 board. These are:

I/O points 0-7 and 8-15

These two 10-pin connectors provide the connections to your input and output devices.

Control connection in (SPx IN)

This 8-pin connector is the control input from the SPLat controller (host) board. It must be connected via a suitable cable assembly to the SPx connector on the host.

Control connection out (SPx OUT)

This 8-pin connector can be connected to another SPx board's control input (SPx IN) connector. In this way several boards can be daisychained together.

PWR

Power is supplied to the board via this connector.

Previous Topic

Next Topic

In This Chapter

XBIO16: Some common terminology

XBIO16: Where to get matching connectors

XBIO16: I/O connections


XBIO16: Some common terminology

If your background is not in the electronics field, we may confuse you a bit with the way we refer to power supply pins, grounds, "common" terminals etc. In electronics we generally quote all voltages in relation to an assumed common or reference terminal. That common terminal is often called "ground", "gnd" or "circuit ground". It may or may not actually be tied to a physical ground point, for instance mains power supply earth.

In SPLat boards we usually label the "ground" terminal "0V". On boards that are powered off an external DC power supply (which is most off-the-shelf boards), 0V is the terminal connected to the negative terminal of the power supply. Measured relative to that pin, therefore, the positive power supply pin will be 24V (or 12V or whatever the supply voltage is) positive. We then say "we have a +12V supply". All boards contain a 5V regulator chip to feed the microprocessor and/or other logic chips. Its output voltage is said to be +5V, again measured relative to the 0V terminal.

Similarly, all input and output voltages are also measured relative to the 0V terminal, and will in virtually all cases be positive voltages. For example, on most SPLat models the input voltage threshold for digital inputs, and analog input and output voltages, are measured relative to 0V.


XBIO16: Where to get matching connectors

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.


XBIO16: I/O connections

Each of the 16 I/O pins on the XBIO16 is bidirectional. That means each one can be used as an input or as an output. In actual fact, each pin is connected onboard to an input circuit and to an output circuit, in parallel. This works because when an output is OFF, it does nothing. Only when an output is turned on does it become a low resistance path to 0V. Hence, if the output circuit is left OFF, the input can respond to an external input device, say a switch or a sensor.

There is a single shared LED indicator per I/O pin. One end of the LED is connected to the + (positive) supply via a resistor, and the other end is connected to the I/O pin. Hence, when the I/O pin is connected to 0V the LED lights up. This can be due either to the output being switched on by your program, or by an external input device connecting the pin to 0V.

The input sensing circuit represents a large resistance value to 0V (some 100's of kOhms). A very small current will flow from the LED circuit through this resistance when the I/O pin is OFF. That means that when the LED should be off it will glow dimly. This is perfectly normal and does not mean there is anything wrong.

Previous Topic

Next Topic

In This Section

XBIO16: Connecting input devices

XBIO16: Connecting output devices


XBIO16: Connecting input devices

The adjacent picture shows the two I/O connectors and two typical input devices.

Each connector contains the connections for 8 I/Os. It also has a positive supply connection labeled + and a common return labeled 0V.

In the picture A is the output of an NPN style sensor connected to I/O 10. B is a simple switch connected to I/O 4. The sensor would also require a positive supply voltage to power it. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

The input is ON when the input pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - (V(supply) - 1.6V) / 4700

The leading minus sign simply means the current is flowing out of the pin. The 1.6V represents the voltage drop of the input indicator LED. The 4700 represents the input pullup resistance.

This equation translates to about 2.2mA with a 12V supply and 4.8mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognize the input as ON when the voltage on the input pin is typically less than 6V. That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input is related to the threshold of the input chip, which has fairly wide uncertainty. Due to this uncertainty the input can only be guaranteed to be ON if it is less than 3V, and can only be guaranteed to be seen as OFF if it is over 9V. The range between 3V and 9V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

How to blow up a bidirectional I/O circuit

You can blow up the I/O circuits by:

  1. Exceeding output current or voltage ratings.
  2. Connecting the I/O pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than 4-500 mA) and turning (programming) the output ON
  3. Connecting the output pin to any negative voltage.
  4. Connecting the output pin to any voltage greater than the positive supply.
  5. Switching an output load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  6. Driving (output) an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.
  7. Driving (output) an incandescent lamp load rated at more than 10% of the output's peak output rating. Incandescent lamps draw a warm-up surge current of many (up to 15) times their rated steady state current.


XBIO16: Connecting output devices

The adjacent picture shows the two I/O connectors and three typical output devices.

Each connector contains the connections for 8 I/Os. It also has a positive supply connection labeled + and a common return labeled 0V.

In the picture A is an LED with a series current limiting resistor (plain LEDs must always have something in series to limit the current!).

B is a relay coil or solenoid. It has a diode across it to prevent inductive kick-backs from the coil. Although the board has suppression diodes onboard, we recommend external diodes to reduce noise problems. The diode can be a 1N4004 or similar 1A rectifier diode.

C is a solid state relay. Note the polarity labels.

The red arrow lines show the positive current path from the power supply, through the coil load and into the I/O pin, then out of the board and back to the power supply common terminal. It is absolutely essential that you provide the wire from the 0V terminal of the appropriate I/O connector back to the supply. Without that wire the current will try and find a path via the control cable back to the control board, and in the process probably burn out something.

Technical explanation: To prevent heavy switching currents from the load flowing back through the controller and causing noise problem, we have isolated the XBIO16 output ground from the controller ground with a resistor.

The (digital) outputs are implemented using the ULN2803A, a chip that contains Darlington transistors. The onboard indicator LEDs are connected directly to the output pin, and returned to the positive supply via (typically) 10K current limiting resistors.

The outputs consist of transistor switches between the output pin and 0V. When the output transistor is off, a multimeter set to DC volts connected between the output and 0V will measure a voltage slightly less than the positive supply voltage. If you set the multimeter to 200mADC, the output LED will turn on and you will get a reading of approximately V/10 milliamps, where V is the supply voltage.

Take care never to short the output pin to the positive supply voltage. If the output turns on the output chip will be instantly destroyed. This includes never doing it with a multimeter set on a current range.

There are catch diodes built into the chip. This means the board will not be damaged if you are driving relay coils, solenoid valves or other inductive loads (within the ratings of the output). We nevertheless recommend external catch diodes connected directly to the relay coil as close as possible to the relay, in order to reduce adverse effects of switching noise. The onboard diodes also mean that the load must not be connected to a supply voltage greater than the board's positive supply voltage.

If you connect the loads (for example relays) separately to the power supply or to a separate power supply, it may be possible to remove the controller's supply connection and still have power fed to the controller via the loads and the onboard protection diodes. This can produce confusing or possibly even dangerous results if the controller gets enough of this "sneak" power to run. It is safer to use the convenience + supply pin on the connector to provide positive voltage to the load.

The maximum allowable output current is 400mA steady state, with brief (100mS) peaks of 500mA allowed. There is also a limit to how much total current one ULN2803A chip can handle. This relates to the internal heating of the chip.

Let's look at 2 extreme cases:

  1. All 8 outputs on one chip conducting the same current
  2. The allowable current concentrated in a few outputs.

Equal currents

The following table lists allowable current versus ambient temperature, when all 8 outputs are conducting the same current at once:

Temperature | Allowable current per output
25°C | 180mA
40°C | 160mA
50°C | 145mA
60°C | 130mA
70°C | 115mA

Concentrated currents

The following table shows the maximum allowable currents, when concentrated in a few outputs, versus temperature

Temp | 1st O/P | 2nd O/P | 3rd O/P | 4th O/P
25°C | 400mA | 400mA | 300mA | 0
40°C | 400mA | 400mA | 150mA | 0
50°C | 400mA | 400mA | 0 | 0
60°C | 400mA | 325mA | 0 | 0
70°C | 400mA | 220mA | 0 | 0

Naturally there are many other possible combinations, but the above should serve as a guide.

The above figures are intended as a guide only. In any event, the absolute maximum operating temperature for the ULN2803A chip is 70°C.

If you feel like doing the actual sums, here's the procedure.

  1. For each output, calculate its output voltage drop from the formula
    V(out) = 0.92 + 1.85 * I(out) [ I(out) in Amperes ]
  2. For each output calculate the dissipation in watts from the formula
    P=V(out) * I(out)
  3. Add up all the dissipation figures to give P(tot) in watts
  4. Calculate the maximum allowable ambient temperature from the formula
    Ta(max) = 125 - P(tot) * 55
  5. If you get an answer over 70°C then use 70°C

This calculation aims at a maximum internal (junction) temperature of 125°C, which is the maximum rating of the chip. There are 2 things to keep in mind with this:

  • A semiconductor will not suddenly die if you exceed the temperature rating by a modest amount
  • As a rule of thumb, every 10°C reduction in temperature doubles the life-time (halves the failure rate).

Hence, an occasional excursion to, or even above, the rated temperature isn't the end of the world. However, for long life the cooler the better. The ULN2803A data sheet contains additional rating curves.

When semiconductor switches fail they are more likely to become stuck ON than OFF.

On the XBIO16 there are two ULN2803A chips, each one associated with one of the I/O connectors. The above ratings and calculations should be applied separately to each one.

How to blow up a bidirectional I/O circuit

You can blow up the I/O circuits by:

  1. Exceeding output current or voltage ratings.
  2. Connecting the I/O pin directly to the positive supply voltage (or to any other voltage source capably of delivering more than 4-500 mA) and turning (programming) the output ON
  3. Connecting the output pin to any negative voltage.
  4. Connecting the output pin to any voltage greater than the positive supply.
  5. Switching an output load that has a large amount of capacitance, i.e. draws large surge current. This could be the case if you tried using an output to activate some other piece of equipment by switching its DC power on and off.
  6. Driving (output) an AC solenoid or relay coil in place of a DC coil. An AC coil will typically draw 10 to 15 times its rated current if accidentally used in a DC circuit.
  7. Driving (output) an incandescent lamp load rated at more than 10% of the output's peak output rating. Incandescent lamps draw a warm-up surge current of many (up to 15) times their rated steady state current.

After you've blown up the outputs

We have thoughtfully fitted the ULN2803A output chip in a socket for you, so you can replace it without soldering. We can supply a 6-pack of ULN2803A chips. Our product code is ULNx6, and is available on our website. Alternatively you can buy the chip wherever fine chips are sold, including many electronics hobbyist stores.. Here are some supplier names and order codes:-

Supplier | Order code
Digikey (USA/International) | Search site
Mouser (USA/International) | 511-E-ULN2803A
Altronics (Australia) | Z3010
Element 14 (formerly Farnell Electronics) (UK, Europe, Australia, NZ) | 705-159, 409-868, 318-7032

The ULN2803A is made by many manufacturers, including Motorola, Texas Instruments, Alegro, Toshiba and ST Microelectronics. You should be able to buy them from most electronic component distributors and even from major electronic hobby stores.

When semiconductor switches fail they are more likely to become stuck ON than OFF.


XBIO16: Connecting to a SPLat controller

The XBIO16 needs to be connected to a SPLat host controller. The connection is made from the SPx connector of the controller to the SPx IN connector on the XBIO16 board. You can cascade several XPice boards by daisychaining SPxOut on one board to SPxIn on the next.

A suitable cable for making this connection is the SPXC60, a 60cm long cable assembly supplied by SPLat Controls. If you buy the XBIO16DK (Developer's Kit), the cable is supplied as part of the kit. Alternatively you can make your own.

You also need to supply power. Normally the board will run off the same power supply as the controller (and any other XPice boards). We recommend running separate wires back to the power supply for each board. This will help maintain a "star point" ground connection, which is a good strategy for reducing noise problems.


XBIO16: Programming

Programming the XBIO16 is very simple. You need an appropriate version of SPLat/PC. SPLat/PC V6.10.2 or later will support the XBIO16 with the MMi99 controller (The MMi99 needs Firmware V3.6 or later). All standard range controllers released subsequent to the MMi99 (after May 2002) will support the board, and the DK versions will be shipped with appropriate support software.

SPLat/PC can be configured to handle the XBIO16 board. The XBIO16 is represented as an extra 16 inputs and an extra 16 outputs. Because each I/O pin on the XBIO16 can be either an input or an output, SPLat/PC shows them as both. You must yourself keep track of which mode you are using each pin in.

Once SPLat/PC is configured for the XBIO16, you program it as you would any other inputs or outputs. There are some minor timing restrictions.

There are two ways you can configure SPLat/PC to recognize the XBIO16 board:

1. Automatic configuration

If you connect the XBIO16 to a compatible SPLat controller, and then connect the controller to SPLat/PC, if SPLat/PC recognises the controller and XBIO16 combination (i.e. you have a recent enough SPLat/PC), SPLat/PC will automatically configure itself. You will see the I/O window grow to accommodate the extra I/O from the XBIO16.

2. Manual configuration

Within SPLat/PC, there is a menu item to configure SPLat/PC to the board you are working with. This menu item is File>Configure>Board Type. Each controller board that can support XPice expansion boards like this one is represented by several variants. These variants relate to the amount of XPice expansion capacity connected to the controller.

For example, for the MMi99 there are the following variants:

MMi99 | Just the controller, no expansion
MMi99+16XPice | Controller plus 16 XPice I/Os
MMi99+32XPice | Controller plus 32 XPice I/Os
MMi99+48XPice | Controller plus 48 XPice I/Os
MMi99+64XPice | Controller plus 64 XPice I/Os

Because the XBIO has, in effect, 16 inputs and 16 outputs, you need to select MMi99+32XPice (assuming you have just one XBIO16 connected).

Input and output numbers

You will need to carefully work out what input and output numbers apply to each XBIO16 I/O point. This will depend on what type of controller the XBIO16 is connected to, and on what other XBIO16 (or other XPice boards) are involved.

For an XBIO16 connected directly to a controller, the input numbers 'run on' directly after the controller's onboard inputs, and the output numbers 'run on' directly after the controller's outputs. On an XBIO16 connected to an MMi99, which has different quantities of inputs and outputs, this means that a given XBIO16 I/O point will have a different number seen as an input and seen as an output.

For an XBIO16 that is connected to another XBIO16, the input and output numbers 'run on' from the XBIO closer to the controller.

The easy way to get this clarified is to actually hook the boards up, connect to SPLat/PC and experimentally activate inputs and outputs. The SPLat/PC I/O window will show what's happening.

Implementing systems with more than 64XPice I/Os

The limit on I/O count implied above is not absolute. Using the SPiceX method you can go considerably further. Tutorial.


XIRO16: 8 inputs and 8 relay outputs (5A)

Previous Topic

Next Topic

In This Chapter

XIRO16: Introduction

XIRO16: Compatibility

XIRO16: Safety considerations

XIRO16: Timing restrictions

XIRO16: Power requirements

XIRO16: Inputs and outputs

XIRO16: Connecting to a SPLat controller

XIRO16: Programming


XIRO16: Introduction

This document provides information on the use of the SPLat XIRO16 expansion board.

The XIRO16 is an I/O expansion board that uses the XPice interface convention. It adds 8 relay outputs and 8 digital inputs to any XPice capable SPLat controller. The board connects to its "host" SPLat controller board via a cable from the SPx expansion connector on the host. It also has an SPx output connector, allowing several XIRO16 (or other XPice expansion boards) to be daisychained.


XIRO16: Compatibility

There are several issues you should be aware of regarding compatibility of the XIRO16 with other SPLat products.

Controller boards

The XIRO16 works with 2nd generation SPLat controllers. At time of writing that means the MMI20x/MMi99 MS12x and SL99 and certain custom SPLat controllers.

That means the following older controllers cannot be used with the XIRO16: SP108, SL88, MMi88, OEM32, OEM36, AJ18 or any custom board designed before 2002. Please consult us regarding custom boards designed after January 2002.

Other expansion boards

The XIRO16 uses the expansion connector (sometimes referred to as the SPx connector) in XPice mode. It can also be used in SPiceX mode, which allows more expansion boards to be used at the cost of less elegant programming. That means you can only use it with other XPice boards, such as the HIO16. You cannot mix it with SPx boards. The SPLat Expansion Framework defines all the various expansion schemes.

Note that this does not preclude Touch of SPice boards. They use a different connector, and do not involve the high speed serial SPx bus.

Program timing

There are some things you need to be aware of concerning the timing of inputs and outputs with the XIRO16. This is documented under Timing Restrictions.


XIRO16: Safety considerations

The XIRO16 board is intended for use as a component part of your product. It is your responsibility to ensure that it is suitable for the use to which you put it, and that it is used safely and complies with all rules and regulations that apply in your location.

In any installation using voltages that exceed the local Extra Low Voltage limit or equivalent (typically 32V or less in most jurisdictions) the installation and wiring should performed by a person qualified to carry out such work in your location.

By proceeding to use the XIRO16 board you are indemnifying SPLat Controls, its agents and resellers against any claims that may arise due to death or injury arising from the use of the XIRO16. If you are not prepared to provide such an indemnity please return the XIRO16 to the supplier for a refund of the purchase price.

Voltage isolation

The primary safety consideration with any control element that is switching voltages greater than the Extra Low Voltage (ELV) limit is how well those voltages (for example 120VAC or 240VAC) are insulated from a human user. In terms of a board like the XIRO16 this reduces to a consideration of:

a) The breakdown (isolation) voltage ratings of the output relays.

b) The physical spacing on the printed circuit board between "hot" conductors and "safe" conductors. This is commonly referred to as creepage distance, i.e. the distance across the normally insulating board that a current would have to "creep" in order to produce a shock hazard.

The relays are rated for 4kV between coil ("safe") and contacts ("hot"). Printed circuit creepage distance is 6mm between relay contacts and electronics. Creepage distance between relay output connectors is small, consequently all contacts on a particular connector must be on the same phase and an open contact must not be regarded as a safety barrier.

If you use metal mounting screws we suggest you use insulating tubular spacers (standoffs). It is advisable to use nuts and/or screw heads that do not extend past the metal pattern surrounding each mounting hole. Alternatively use insulating washers under the nuts or screw heads. We strongly urge you to use all 7 mounting holes to ensure firm mounting and to support the board while connections are made and unmade. The DK version of the board is supplied with snap-in nylon standoffs.

When mounting the board you must take into account the proximity of the exposed printed circuit conductors to the mounting surface (probably a metal surface).

Wiring (insulation) considerations

In many applications the input wring and the wiring to the relays will be at a dangerous voltage. At the same time it will necessarily have single layer insulation. Safety regulations in most jurisdictions, and plain engineering common sense, dictate that you should physically separate such wiring from the ELV wiring.

High currents and fusing

You must make sure that the connecting wire you use is appropriately rated for the load current. You must also make sure the crimp connectors you use are selected to match the wire gauge, that they are rated for the current and that they are correctly crimped. Failure to observe the precautions could result in an overheated contact leading to a dangerous situation.

To prevent fault currents from overheating wires and melting the insulation it is mandatory in most jurisdictions that circuits carrying high currents be suitably fused to fault trip at a current less than or equal to the rating of the wires.


XIRO16: Timing restrictions

The XIRO16 connects to the main controller board via a high speed serial data bus and a protocol called XPice, which is implemented on 2nd generation SPLat controllers (see SPLat Expansion Framework). That means that input changes and output changes are updated only when the controller reads and writes bus updates.

2nd generation controllers at time of writing are the MMi20x/MMi99 and the SL99. These perform XPice updates every millisecond. That means the effect of any programmed output change (ON, OFF, Output etc) will not take effect for up to 1mS after the instruction is executed. This will add to the several mS mechanical response time of the relays.

Similarly, any change on the inputs will not be seen by your program for up to 1mS after it happens. That has very little practical significance unless you are using InputF or InputFM instructions (all other input type instructions have contact debounce that introduces a delay of up to 20mS).


XIRO16: Power requirements

The XIRO16 board may be powered off the same power supply as the SPLat controller board it serves or off a different supply. The XIRO16 requires a supply voltage of 12-28VDC, with an absolute maximum rating of 32VDC. It typically draws up to 300mA with all relays activated.

Note: If you use two power supplies, the expansion board(s) must be powered up at the same time as, or before, the controller.

It is absolutely essential that none of the current from the external inputs flows through the cable back to the controller. For this reason the 0V pin on the XIRO16 power connector must be wired independently back to the main power supply. If you use separate power supplies you must tie their negative (0V) terminals together and to the 0V terminals of the controller and XIRO16. If you fail to do this you will at best experience erratic performance and at worst damage either the XIRO16, the controller board or both.

You should supply power for the board itself via the 2-pin power connector. Note that the + pins on the power and input connectors are joined together on the board, as are the 0V pins. It is a good idea to separately wire all 0V pins back to the power supply common. The + supply is brought out to the input connector as a convenience, if you are using powered sensors like proximity switches.


XIRO16: Inputs and outputs

Xiro16: Full board

The picture above shows the locations of the connectors on the XIRO16 board. These are:

Relays 0, 1 and 2 and relays 3, 4 and 5 (SPST)

These two 6-pin connectors access the contacts on the six SPST (normally open) relays. Each relays' contacts come out to a pair of pins.

Relays 6 and 7 (SPDT)

These two 3-pin connectors access the SPDT (change-over) contacts of relays 6 and 7. The middle pin is common, while the normally open (NO) and normally closed (NC) contacts are as marked.

Inputs

The 8 inputs are on a 10-pin connector. A 0V and +supply pin are provided for convenience. An input is ON when the input pin is connected to 0V.

Control connection in (SPx IN)

This 8-pin connector is the control input from the SPLat controller (host) board. It must be connected via a suitable cable assembly to the SPx connector on the host.

Control connection out (SPx OUT)

This 8-pin connector can be connected to another SPx board's control input (SPx IN) connector. In this way several boards can be daisychained together.

PWR

Power is supplied to the board via this connector.

Previous Topic

Next Topic

In This Chapter

XIRO16: Some common terminology

XIRO16: Where to get matching connectors

XIRO16: Connecting input devices

XIRO16: Connecting output devices

XIRO16: Relay ratings


XIRO16: Some common terminology

If your background is not in the electronics field, we may confuse you a bit with the way we refer to power supply pins, grounds, "common" terminals etc. In electronics we generally quote all voltages in relation to an assumed common or reference terminal. That common terminal is often called "ground", "gnd" or "circuit ground". It may or may not actually be tied to a physical ground point, for instance mains power supply earth.

In SPLat boards we usually label the "ground" terminal "0V". On boards that are powered off an external DC power supply (which is most off-the-shelf boards), 0V is the terminal connected to the negative terminal of the power supply. Measured relative to that pin, therefore, the positive power supply pin will be 24V (or 12V or whatever the supply voltage is) positive. We then say "we have a +12V supply". All boards contain a 5V regulator chip to feed the microprocessor and/or other logic chips. Its output voltage is said to be +5V, again measured relative to the 0V terminal.

Similarly, all input and output voltages are also measured relative to the 0V terminal, and will in virtually all cases be positive voltages. For example, on most SPLat models the input voltage threshold for digital inputs, and analog input and output voltages, are measured relative to 0V.


XIRO16: Where to get matching connectors

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.


XIRO16: Connecting input devices

The adjacent picture shows the input connector and two typical input devices.

The connector contains the connections for the 8 input. It also has a positive supply connection labeled + and a common return labeled 0V.

In the picture A is the output of an NPN style sensor connected to input 6. B is a simple switch connected to input 4. The sensor is getting its power from the XIRO16 input connector. See the manufacturer's data for connection details of the sensor you are using. The terminal labeled with a minus sign above will be the manufacturer's designated negative or common terminal, and the "O" terminal will be the designated output terminal of the sensor.

The input is ON when the input pin is connected to 0V (Gnd). Under that condition a (positive) current flows out of the input pin, through the external circuit, to 0V. The magnitude of that current depends on the supply voltage. Assuming a dead short between the input pin and 0V, the current is given by the equation

I(in) = - (V(supply) - 1.6V) / 4700

The leading minus sign simply means the current is flowing out of the pin. The 1.6V represents the voltage drop of the input indicator LED. The 4700 represents the input pullup resistance.

This equation translates to about 2.2mA with a 12V supply and 4.8mA with a 24V supply. This is low enough to be compatible with all off-the-shelf NPN style sensors. The inputs are not compatible with PNP style sensors.

Take care when driving the inputs from switch or relay contacts. The problem is "contact wetting" (or a lack of it) and due not to too much current but to too little current. You should select contacts that are designed for low level switching. There is an article about this here.

You should also be aware that the board will only recognize the input as ON when the voltage on the input pin is typically less than 6V. That means the input needs to be a "fairly solid" connection to 0V.

A word of caution: In practice the threshold voltage of the input is related to the threshold of the input chip, which has fairly wide uncertainty. Due to this uncertainty the input can only be guaranteed to be ON if it is less than 3V, and can only be guaranteed to be seen as OFF if it is over 9V. The range between 3V and 9V is a "no man's land" where behaviour is not guaranteed. That means that you may determine experimentally that something works OK on a prototype, but that gives you no right to assume it will therefore work on all future units.

How to blow up the inputs

You can blow up the inputs by:

  1. Applying a positive voltage 5V above the positive supply. This can damage the indicator LED instantaneously by reverse voltage breakdown;
  2. Applying a negative voltage of more than (32V - The positive supply voltage). This will damage the input pullup resistors due to overheating. This is not a sudden death failure. The pullup resistors are rated at 200mW.


XIRO16: Connecting output devices

The outputs on the XIRO16 are relays. You can wire these as you would any other relay contact. Six of the relays are SPST, and two are SPDT.

If you intend to use this board to switch 120/240VAC circuits, you must check that you are in compliance with all electrical safety regulation in your country. The board provides 6mm tracking distance between relay contacts and the electronics. All relays on a given connector should be switching the same line phase. Under no circumstances mix mains (power line) circuits with extra low voltage circuits on the same side of the board. Please see also our product disclaimer.

The diagram above shows 4 of the XIRO16 relays connected to various AC loads. The relays are shown in the de-activated (output OFF, no power to the board) position. The AC power source is shown as L for Line and N for Neutral. The voltage may be 24VAC or 240VAC or anything in between. However, you must observe the following rules:

  1. For voltages over 24VAC make sure you are complying with all local regulations concerning wiring of lethal voltages.
  2. Never, ever, use different supply voltages or mains phases on one group of relays. The relays show above constitute one group. The XBIO16 has two such groups of 4 relays.

The diagram shows three loads:

Coil

Output 2 is driving a coil. This could be a contactor or a solenoid valve. We have shown a resistor-capacitor snubbing network across the coil. It is generally assumed that for AC loads a resistive-capacitive snubber will reduce the damage due to arcing. In our experience with comprehensive tests of driving a large contactor, this is not necessarily true. We found the R-C snubber actually did more harm than good. There is some excellent material here on this and other relay related topics. The main reason for using a snubber on relay contacts is to suppress arcing and thereby extend the contact life. However, there is another reason, namely suppression of electromagnetic interference (EMI). Contactor coils in particular seem to generate a lot of EMI, sometimes enough to upset the SPLat processor and cause erratic behaviour. A small snubber comprising a 10nF 3kV ceramic capacitor in series with a 100Ohm 1W resistor will usually clean this up.

Incandescent lamp

Output 1 is driving a small incandescent light bulb. The thing to watch here is that a filament lamp can draw up to 20x is nominal current rating at turn-on. This inrush current can lead to early failure of the relay contacts. Allowing for this factor a 5A contact should not drive more than a 250mA lamp!

Small reversible AC motor

Outputs 0 and 6 are between them driving a small AC motor of the type that has 3 connection wires to allow it to be driven in both directions. Output 6 is a SPDT (single pole change-over) relay connected to feed one or the other side of the motor. Relay 0 switches the main power to relay 6, for on/off control.


XIRO16: Relay ratings

The relay contact ratings depend on whether they are switching AC or DC, and differ for normally open and normally closed contacts.

The following is a summary of the relay contact ratings. Please refer to the relay data sheet (relay type JZC33F) for full information:

Normally open contacts on all relays: 5A @ 125VAC*, 3A @ 250VAC*, 5A @ 30VDC

Normally closed contacts on relays 6 and 7: 2A @ 125VAC*, 1A @ 250VAC*, 1A @ 30VDC

These ratings are for resistive loads. If you use the board with inductive loads such as motors or solenoids, there will be additional strain placed on the relay contacts by the spark generated when the contact opens. Derate the contacts by at least 40%.

If you intend to use this board to switch 120/240VAC circuits, you must check that you are in compliance with all electrical safety regulation in your country. The board provides 6mm tracking distance between relay contacts and the electronics. All relays on a given connector should be switching the same line phase. Under no circumstances mix mains (power line) circuits with extra low voltage circuits on the same side of the board. Please see also our product disclaimer.

If you are driving DC inductive loads (say a contactor with a 24VDC coil) you can reduce the strain on the relay contacts by connecting a diode across the load, cathode to the positive load terminal. This will prevent the spark from forming, at the expense of slowing down the turn-off speed, and allow you to use the relay to the full resistive load rating. A 1N4004 will work fine for any load the relay is able to drive. (Don't attempt this with AC loads!).

It is generally assumed that for AC loads a resistive-capacitive snubber will reduce the damage due to arcing. In our experience with comprehensive tests of driving a large contactor, this is not necessarily true. We found the R-C snubber actually did more harm than good. There is some excellent material here on this and other relay related topics.

The full data sheet for the relays is located in the file resources area of the SPLat Resources CD. Look for jzc33f.pdf


XIRO16: Connecting to a SPLat controller

The XIRO16 needs to be connected to a SPLat host controller. The connection is made from the SPx connector of the controller to the SPx IN connector on the XIRO16 board. You can cascade several XPice boards by daisychaining SPxOut on one board to SPxIn on the next.

A suitable cable for making this connection is the SPXC60, a 60cm long cable assembly supplied by SPLat Controls. If you buy the XIRO16DK (Developer's Kit), the cable is supplied as part of the kit. Alternatively you can make your own.

You also need to supply power. Normally the board will run off the same power supply as the controller (and any other XPice boards). We recommend running separate wires back to the power supply for each board. This will help maintain a "star point" ground connection, which is a good strategy for reducing noise problems.


XIRO16: Programming

All standard range controllers released subsequent to the MMi99 (such as SL99) will support the board, and the DK versions of those controllers ship with appropriate support software.

SPLat/PC can be configured to handle the XIRO16 board. The XIRO16 is represented as an extra 8 inputs and an extra 8 outputs.

Once SPLat/PC is configured for the XIRO16, you program it as you would any other inputs or outputs. There are some minor timing restrictions.

There are two ways you can configure SPLat/PC to recognise the XIRO16 board:

1. Automatic configuration

If you connect the XIRO16 to a compatible SPLat controller, and then connect the controller to SPLat/PC, if SPLat/PC recognises the controller and XIRO16 combination (i.e. you have a recent enough SPLat/PC), SPLat/PC will automatically configure itself. You will see the I/O window grow to accommodate the extra I/O from the XIRO16.

2. Manual configuration

Within SPLat/PC, there is a menu item to configure SPLat/PC to the board you are working with. This menu item is File>Configure>Board Type. Each controller board that can support XPice expansion boards like this one is represented by several variants. These variants relate to the amount of XPice expansion capacity connected to the controller.

For example, for the MMi200 there are the following variants:

MMi200 | Just the controller, no expansion
MMi200+16XPice | Controller plus 16 XPice I/Os
MMi200+32XPice | Controller plus 32 XPice I/Os
MMi200+48XPice | Controller plus 48 XPice I/Os
MMi200+64XPice | Controller plus 64 XPice I/Os

Because the XIRO16 has 8 inputs and 8 outputs, for a total of 16 I/O points, you need to select MMi200+16XPice (assuming you have just one XIRO16 connected). Similar for other controllers (like SL99).

Input and output numbers

You will need to carefully work out what input and output numbers apply to each XIRO16 I/O point. This will depend on what type of controller the XIRO16 is connected to, and on what other XIRO16 (or other XPice boards) are involved.

For an XIRO16 connected directly to a controller, the input numbers 'run on' directly after the controller's onboard inputs, and the output numbers 'run on' directly after the controller's outputs. On an XIRO16 connected to an MMi200, which has different quantities of inputs and outputs, this means that the 8 inputs and 8 outputs will not have identical numbers.

For an XIRO16 that is connected to another XIRO16, the input and output numbers 'run on' from the XIRO16 closer to the controller.

The easy way to get this clarified is to actually hook the boards up, connect to SPLat/PC and experimentally activate inputs and outputs. The SPLat/PC I/O window will show what's happening.

Implementing systems with more than 64XPice I/Os

The limit on I/O count implied above is not absolute. Using the SPiceX method you can go considerably further. Tutorial.


HIO16: 8 high power relays (20A/1HP) and 8 isolated inputs

Note: This document is not yet complete. It may contain inaccuracies.

Previous Topic

Next Topic

In This Chapter

HIO16: Introduction

HIO16: Safety considerations

HIO16: Compatibility

HIO16: Timing considerations

HIO16: Power requirements

HIO16: Inputs and outputs

HIO16: Connecting to a SPLat controller

HIO16: Programming


HIO16: Introduction

Note: This document is not yet complete. It may contain inaccuracies.

The HIO16 is an XPice expansion board for those SPLat controllers that support XPice. It provides 8 high power relay outputs and 8 optically isolated AC/DC sensing logic inputs. Special features include:

  • Switching of motor loads up to 20A running or 80A inrush.
  • Isolated inputs that can handle 24V through 240V, AC or DC.


HIO16: Safety considerations

Note: This document is not yet complete. It may contain inaccuracies.

The HIO16 board is intended for use as a component part of your product. It is your responsibility to ensure that it is suitable for the use to which you put it, and that it is used safely and complies with all rules and regulations that apply in your location.

In any installation using voltages that exceed the local Extra Low Voltage limit or equivalent (typically 32V or less in most jurisdictions) the installation and wiring should performed by a person qualified to carry out such work in your location.

By proceeding to use the HIO16 board you are indemnifying SPLat Controls, its agents and resellers against any claims that may arise due to death or injury arising from the use of the HIO16. If you are not prepared to provide such an indemnity please return the HIO16 to the supplier for a refund of the purchase price.

Voltage isolation

The primary safety consideration with any control element that is switching voltages greater than the Extra Low Voltage (ELV) limit is how well those voltages (for example 120VAC or 240VAC) are insulated from a human user. In terms of a board like the HIO16 this reduces to a consideration of:

a) The breakdown (isolation) voltage ratings of the input optocouplers and the output relays.

b) The physical spacing on the printed circuit board between "hot" conductors and "safe" conductors. This is commonly referred to as creepage distance, i.e. the distance across the normally insulating board that a current would have to "creep" in order to produce a shock hazard.

The optocouplers are rated at 5kV rms input to output isolation (1 minute). The full data sheet is supplied on the resources CD. Look for pc815_e.pdf.

The relays are rated for 4.5kV between coil ("safe") and contacts ("hot").

The following diagram details the creepage distance for various parts of the board.

(Diagram being prepared)

If you use metal mounting screws we suggest you use insulating tubular spacers (standoffs). It is advisable to use nuts and/or screw heads that do not extend past the metal pattern surrounding each mounting hole. Alternatively use insulating washers under the nuts or screw heads. We strongly urge you to use all 7 mounting holes to ensure firm mounting and to support the board while connections are made and unmade. The DK version of the board is supplied with snap-in nylon standoffs.

When mounting the board you must take into account the proximity of the exposed printed circuit conductors to the mounting surface (probably a metal surface).

Wiring (insulation) considerations

In many applications the input wring and the wiring to the relays will be at a dangerous voltage. At the same time it will necessarily have single layer insulation. Safety regulations in most jurisdictions, and plain engineering common sense, dictate that you should physically separate such wiring from the ELV wiring.

High currents and fusing

The output relays on the HIO16 are rated for high currents, up to 20A running and 80A inrush. You must make sure that the connecting wire you use is appropriately rated for the load current. You must also make sure the crimp connectors you use are selected to match the wire gauge, that they are rated for the current and that they are correctly crimped. Failure to observe the precautions could result in an overheated contact leading to a dangerous situation.

To prevent fault currents from overheating wires and melting the insulation it is mandatory in most jurisdictions that circuits carrying high currents be suitably fused to fault trip at a current less than or equal to the rating of the wires.


HIO16: Compatibility

Note: This document is not yet complete. It may contain inaccuracies.

There are several issues you should be aware of regarding compatibility of the HIO16 with other SPLat products.

Controller boards

The HIO16 works with 2nd generation SPLat controllers. At time of writing that means the MMi99/20x, MS12x and SL99 and certain custom SPLat controllers.

The following older controllers cannot be used with the HIO16: SP108, SL88, MMi88, OEM32, OEM36, AJ18 or any custom board designed before 2002. Please consult us regarding custom boards designed after January 2002.

Other expansion boards

The HIO16 uses the expansion connector (sometimes referred to as the SPx connector) in XPice mode. It can also be used in SPiceX mode, which allows more expansion boards to be used at the cost of less elegant programming. That means you can only use it with other XPice boards, such as the XBIO16. You cannot mix it with SPx boards. The SPLat Expansion Framework defines all the various expansion schemes.

Note that this does not preclude Touch of SPice boards. They use a different connector, and do not involve the high speed serial SPx bus.

Program timing

There are some things you need to be aware of concerning the timing of inputs and outputs with the HIO16. This is documented under Timing Restrictions.


HIO16: Timing considerations

Note: This document is not yet complete. It may contain inaccuracies.

The HIO16 connects to the main controller board via a high speed serial data bus and a protocol called XPice, which is implemented on 2nd generation SPLat controllers (see SPLat Expansion Framework). That means that input changes and output changes are updated only when the controller reads and writes bus updates.

The output relays take approximately 20mS to pull in (turn on) and 10mS to release (turn off)

The input circuits on the HIO16 contain built in timers to allow operation on AC as well as DC. The time delays between an input change and your program seeing the change depend on whether the input is 50Hz AC, 60Hz AC or DC, and on the type of input instruction. The following table shows the min/max delays for each instance:

 | InputF, InputFM; (fast input) |  | Input, WaitOn etc (debounced input)
 | ON | Off | ON | Off
50Hz | 0 - 11mS | 20 - 31mS | 10 - 31mS | 30 - 51mS
60Hz | 0 - 10.3mS | 21.7 - 31mS | 10 - 29.3mS | 31.7 - 51mS
DC | 0 - 1mS | 30 - 31mS | 10 - 11mS | 30 - 51mS


HIO16: Power requirements

Note: This document is not yet complete. It may contain inaccuracies.

The HIO16 needs a 24VDC supply connected to its power connector. Current draw is 350mA maximum.

Note: If you use two power supplies, the expansion board(s) must be powered up at the same time as, or before, the controller.


HIO16: Inputs and outputs

Note: This document is not yet complete. It may contain inaccuracies.

Previous Topic

Next Topic

In This Section

Some common terminology

HIO16: Where to get matching connectors

HIO16: Connecting input devices

HIO16: Connecting to output relays

HIO16: Relay ratings


HIO16: Where to get matching connectors

Note: This document is not yet complete. It may contain inaccuracies.

If you are buying your boards in the OEM version, you will need to supply your own matching connectors. In many situations this is consistent with having a pre-made wiring loom made, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are 0.2" or 5.08mm pitch. Matching female connectors are available from Molex as part number 10-01-3??6 (housing) and 08-07-1031 (contacts). The ?? represents the number of contact positions.

The matching power connector is Molex 09-91-0200 with the same contacts as the larger pitch input connectors.

The connection tabs on top of the relays are standard 6.3mm Quick Connects. You should be able to buy matching slip on connectors and crimping tools from any electrical supplier. Make sure you buy terminals rated for the current you will be running through them.


HIO16: Connecting input devices

Note: This document is not yet complete. It may contain inaccuracies.

The 8 optically isolated logic inputs on the HIO16 are grouped into 2 groups of 4 inputs. Each group of 4 shares an Input Common, labeled IC1 and IC2 respectively. These input commons are not connected to each other or any other part of the circuit.

An input becomes activated (ON) when the input terminal is more than about 12V (24V guaranteed) positive relative to the common terminal. The input circuit can tolerate up to 400V positive, and it has a series diode that will resist up to 700V in the opposite polarity. With an AC voltage applied at 50Hz or 60Hz, the output of the optoisolator will pulsate on for each positive half-cycle. An onboard retriggerable 30mS timer stretches the pulses together to produce a continuous input signal to the SPLat controller whenever there is an AC input.

The input resistance is 47K for positive voltages and practically an open circuit for negative voltages. That means the circuit draws very little current. If you are driving the input off non-gold plated switch or relay contacts, the tiny current drawn by the HIO16 inputs may be insufficient to keep the contacts clean. Google for wetting current. There is an article about this here.

The above diagram shows how typical inputs can be connected. The top group of inputs are connected to a switch contact (input 0) and a PNP type sensor (input 2). A 24V DC power supply provides the necessary power for the sensor and to drive the input off the contact. Notice that the negative terminal of the power supply is connected to the input common (IC1). The other way around will not work.

The bottom group of inputs has one input driven off a contact with an AC supply. The AC can be between 24VAC and 240VAC. Notice that the AC neutral wire is connected to the input common (IC2). If you wired it the other way around there would be a small possibility of false triggering, especially on the high end of the voltage range and/or if there is a lot of electrical noise present. For similar reasons we suggest that you avoid dressing the input wires in close proximity to the relay wiring.

Note: We do not recommend that you can mix low voltage and high voltage across the two groups of inputs. See safety considerations.


HIO16: Connecting to output relays

Note: This document is not yet complete. It may contain inaccuracies.

The output relays on the HIO16 all have a single normally open contact. Connection is via the quick connect tabs on top of the relays.

Because of the possibly high voltages and currents involved, you must observe certain precautions:

  • Make sure the wires are led directly away from the tops of the relays, and do not pass close to other parts of any of the SPLat boards. You should also tie the two wires in each pair together to reduce the size of loops (which can radiate magnetically). This will minimise the risk of noise coupling from high powered output circuits back into the HIO16 or the controller. The board has been deliberately laid out with the relays clustered one end, so you can take the relay wires away from the board without passing them over other parts of the board.
  • All wiring should be performed by a person qualified to perform such work in your geographic location.
  • Observe all local regulations regarding wire gauge, insulation and fusing. Note in particular that there is no fusing on the board, and the regulations in most places mandate fusing at less than the ampere capacity of the interconnecting wiring. That means you must provide external fusing.

The above photo shows how not to wire an output relay. The problems here are:

a) The relay current is flowing through what amounts to a large loop or coil with the board's logic circuitry right in the middle. This maximises the chances of any magnetic fields generated in the wires inducing currents into the board and upsetting the circuitry on the board. Such magnetic fields are generated by the current flowing through the wires.

b) The wires pass low down and close to the board. This maximises the chances of electric fields generated in the wires from radiating into the circuitry on the board and causing problems. Such electric fields are caused by sudden voltage changes in the wires when the relays switch (especially switching off). The voltages induced when switching off a motor can amount to brief "spikes" of thousands of volts with very rapid rise and fall times.

c) The close proximity of the wires to the board also potentially compromise the safety rating of the setup. If they actually touch the components it means you only have the single layer of wire insulation between the high voltage on the relay contacts and the "safe" ELV side.

The above picture shows a better way of doing it. Here the wires from the relay are routed up and away from the board, and the have been tied together to prevent a loop of wire from being formed. In a real situation it would also usually be necessary to physically restrain the wires so they cannot accidentally move and touch the board.


HIO16: Relay ratings

Note: This document is not yet complete. It may contain inaccuracies.

The following is a summary of the relay contact ratings. For full details please see the relay data sheet (type jqx-102f):

  • Current rating (steady state); 20A at 240VAC
  • Surge rating (motor turn-on): 80A at 240V (0.3 seconds)
  • Horse power: 1HP @ 120VAC, 2HP @ 240VAC.

The relays are characterized specifically for AC motor loads. It is generally held that for AC loads a resistive-capacitive snubber will reduce contact damage due to arcing. In our experience with comprehensive tests of driving a large contactor, this is not necessarily true. We found an R-C snubber actually did more harm than good. There is some excellent material here on this and other relay related topics.

If you are driving DC inductive loads (say a 24VDC motor) you can reduce the strain on the relay contacts by connecting a diode across the load, cathode to the positive load terminal. This will prevent a spark from forming at turn-off. On a motor it will also act as a dynamic brake, making it stop faster. You should use a diode with a reverse voltage rating (PIV) greater than the DC supply voltage. For a non-motor load the diode should have a half cycle surge current rating equal to the DC load current. A 1N5404 will cover most applications. For motor loads the diode may need a higher current rating, because there will be stored rotational energy as well as electrical energy dissipated in the diode each time the motor stops. Don't attempt to use diodes with AC loads.


HIO16: Connecting to a SPLat controller

Note: This document is not yet complete. It may contain inaccuracies.

The HIO16 needs to be connected to a SPLat host controller. The connection is made from the SPx connector of the controller to the SPx In connector on the HIO16 board. You can cascade several XPice boards by daisychaining SPxOut on one board to SPxIn on the next.

A suitable cable for making this connection is the SPXC60, a 60cm long cable assembly supplied by SPLat Controls. If you buy the HIO16DK (Developer's Kit), the cable is supplied as part of the kit. Alternatively you can make your own.

You also need to supply power. Normally the board will run off the same power supply as the controller (and any other XPice boards). We recommend running separate wires back to the power supply for each board. This will help maintain a "star point" ground connection, which is a good strategy for reducing noise problems.


HIO16: Programming

Note: This document is not yet complete. It may contain inaccuracies.

All standard range controllers released subsequent to the MMi99 (such as SL99, MMi200 and MS12) will support the board, and the DK versions of those controllers ship with appropriate support software.

SPLat/PC can be configured to handle the HIO16 board. The HIO16 is represented as an extra 8 inputs and an extra 8 outputs.

Once SPLat/PC is configured for the HIO16, you program it as you would any other inputs or outputs. There are some minor timing restrictions.

There are two ways you can configure SPLat/PC to recognise the HIO16 board:

1. Automatic configuration

If you connect the HIO16 to a compatible SPLat controller, and then connect the controller to SPLat/PC, if SPLat/PC recognises the controller and HIO16 combination (i.e. you have a recent enough SPLat/PC), SPLat/PC will automatically configure itself. You will see the I/O window grow to accommodate the extra I/O from the HIO16.

2. Manual configuration

Within SPLat/PC, there is a menu item to configure SPLat/PC to the board you are working with. This menu item is File>Configure>Board Type. Each controller board that can support XPice expansion boards like this one is represented by several variants. These variants relate to the amount of XPice expansion capacity connected to the controller.

For example, for the MMi99 there are the following variants:

MMi99 | Just the controller, no expansion
MMi99+16XPice | Controller plus 16 XPice I/Os
MMi99+32XPice | Controller plus 32 XPice I/Os
MMi99+48XPice | Controller plus 48 XPice I/Os
MMi99+64XPice | Controller plus 64 XPice I/Os

Because the HIO16 has 8 inputs and 8 outputs, for a total of 16 I/O points, you need to select MMi99+16XPice (assuming you have just one HIO16 connected). Similar for other controllers (like SL99).

Input and output numbers

You will need to carefully work out what input and output numbers apply to each HIO16 I/O point. This will depend on what type of controller the HIO16 is connected to, and on what other HIO16 (or other XPice) boards are involved.

For an HIO16 connected directly to a controller, the input numbers 'run on' directly after the controller's onboard inputs, including SPice pins, and the output numbers 'run on' directly after the controller's outputs including SPice pins. On an HIO16 connected to an MMi99, which has different quantities of inputs and outputs, this means that the 8 inputs and 8 outputs will not have identical numbers.

For an HIO16 that is connected to another HIO16, the input and output numbers 'run on' from the HIO16 closer to the controller.

The easy way to get this clarified is to actually hook the boards up, connect to SPLat/PC and experimentally activate inputs and outputs. Don't do this with real loads connected to the relays, rather use a multimeter to check the state of the relays contacts. The SPLat/PC I/O window will show what's happening.

Implementing systems with more than 64XPice I/Os

The limit on I/O count implied above is not absolute. Using the SPiceX method you can go considerably further. Tutorial.


Product documentation: SPice add-on boards

spice intro pic

SPice (or Touch of SPice) boards are small add-on boards that can be added "piggy back" to the SPice connector on a SPLat controller board.

The SPice connector on a controller board makes a relatively small number of otherwise unused processor pins available for use. These pins are generally brought out to the SPice connector with minimal conditioning. A SPice board provides the real-world interfacing to make these pins usable to the outside world. The SPice board will typically provide some kind of special purpose interface(s). A controller can only have one SPice board.

SPice boards are usually small, about the size of a business card, and relatively inexpensive to design and make. They therefore offer a means of providing affordable custom or special purpose interfacing. A custom SPice board can be economical in quantities as low as 15 off.

Previous Topic

Next Topic

In This Chapter

SPice10200 - Temperature measurement and fluid level switching

SPice10203 - 4 extra push buttons

SPice10204 - Current transformer

SPice10205 - 6 fluid level switches

SPice10206 - 3rd party embedded web server support

SPice10209 - Type K thermocouple interface

SPice10211 - Analog I/O

SPice10212 - 3-phase VAC measurement

SPice10213: DC motor control

SPice10214 - Potentiometer interface

SPIce 10218 - 8 channel analog input multiplexer


SPice10200 - Temperature measurement and fluid level switching

Previous Topic

Next Topic

In This Chapter

SPice 10200: Introduction

SPice 10200: A quick tour

SPice 10200: Fitting to controller

SPice 10200: Power supply requirements

SPice 10200: External connections

SPice 10200: About water level detection

SPice 10200: About temperature measurement

SPice 10200: Programming


SPice 10200: Introduction

The SPice 10200 is an add-on board for SPLat controllers that provides three channels of temperature measurement and two channels of fluid level switching.

The temperature measurement uses user-supplied low cost thermistors to perform temperature measurement at very low cost. It is suitable for applications where modest accuracy and resolution are required. Typically achievable accuracy and resolution figures are 0.2°C to 0.5°C with calibration. Without calibration accuracy is typically 2-4°C. The system is suitable for temperatures from -30°C to +300°C, though thermistors are available that will work up to 700°C

The fluid level sensing works on the principle of detecting an electric current flow between two electrodes submersed in the fluid. Hence, it works on electrically conductive fluids such as water, milk, detergent etc. It will not work with non-conductive fluids like ultra-pure water or oils.

The SPice10200 is a Touch of SPice board. This is the name applied to one of the several expansion systems supported within the SPLat Expansion Framework. The board may be used with MMi99/200, MS12 and SL99 controllers. It does not follow that future controllers will also support it ... the Touch of SPice system is a very low cost expansion scheme that does not guarantee compatibility across the whole family of SPLat controller products.


SPice 10200: A quick tour

SPice10200 features called out

The above picture shows a SPice 10200 board plugged into an SL99 controller, with all the main features called out.

There are two connectors, one for thermistors and one for conductivity electrodes.

Thermistor connector

The 6-pin connector is for the 3 thermistors. Each thermistor goes between a Gnd (0V) pin and Therm1, Therm2 or Therm3.

Conductivity connector

The 4-pin connector is for the conductivity electrodes. Electrode design is covered later. Each conductivity channel requires two electrodes submersed in the fluid to be detected. One electrode is at circuit Ground (0V) potential, which in the case of a metallic vessel would be the body of the vessel. The other electrode of the pair is referred to as "High", and would be mounted so the fluid covers it at the fluid level you wish to detect.

Conductivity threshold adjustment

This is a small potentiometer that can be used to adjust the sensitivity of the conductivity switches. Turning this control clockwise will de-sensitize the circuit, i.e. mean a lower resistance (more conduction) is required between the electrodes to trip the detector.

Links LK1, LK2 and LK3

These are used to select the value of feed resistor Vfeed in the thermistor circuits. With the jumper in place, Vfeed is 9.0909K. With the jumper removed, it is 100K. Most applications will have the jumper in place.

Mounting hole

This is referred to later. The picture was taken with the SPice 10200 plugged into an SL99, but without any mounting hardware yet installed.


SPice 10200: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. If you are using an MMi controller, remove the two option links
  2. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  3. Identify the matching single in-line pin connector on the controller board.
  4. Experimentally plug the two boards together, taking care to align the connectors
  5. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  6. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  7. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice 10200: Power supply requirements

The board draws its power from the host controller board. The supply voltage needs to be at least 2V more than the programmed thermistor drive voltage. If the temperature channels are not used, the supply voltage can be anything over 12V.


SPice 10200: External connections

The SPice 10200 has two connectors for external connections. These are described in SPice 10200: A quick tour.

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.


SPice 10200: About water level detection

The SPice 10200 has two channels of water level switching. These work on the principle that if two electrodes have a voltage applied between them, and they are submersed in an electrically conductive fluid, a detectable current will flow. The amount of current that flows is proportional to the surface area of the electrodes (larger electrodes = more current) and inversely proportional to the distance between the electrodes (more spacing = less current). The current also depends on the conductivity of the fluid (very pure water = small current, very salty water = much more current). For a given electrode geometry and fluid the overall conduction can be defined as a resistance value.

The conductivity circuit on the SPice 10200 is designed to switch on and off at a certain resistance. The board has an adjustment for setting this resistance sensitivity. The adjustment range is approximately 2.2K to 27K. The circuit applies an AC voltage to the electrodes (DC can cause electrolysis problems).

Each of the two conductivity channels has a 2-wire connection, for 2 electrodes. On each channel one electrode is connected on the board to 0V or ground.

You have to design and provide your own electrodes. In the main this requires mainly common sense plus a bit of experimentation.

There are two main classes of electrode design, which are governed by the nature of the vessel or pipework in which you want to detect the presence or absence of fluid. If the vessel is made of an electrically insulating material you will need two electrodes. If the vessel is made of metal, you will very possibly be able to use the vessel itself as one electrode. In the latter case you have to make sure the vessel is connected to the 0V (ground) electrode connection because the vessel will usually already be at ground potential.

It is usually advisable to use stainless steel for the electrodes. In many cases something as simple as two stainless steel machine screws through the side of a plastic vessel will do, with wires connected on the outside. In a metal vessel, a single screw with an insulated mounting arrangement, plus a wire from the screw and one from the body of the vessel (don't rely on ground conduction for the return path).

There are two parameters that affect the performance of your electrodes: How much they conduct when they are in fluid (wet) and how little they conduct when they are not in the fluid (dry).

How could dry electrodes conduct? Because they may not be dry! Wetting, residue build-up, even solid matter or foam, can all form unexpected and unwanted conduction paths when the electrodes are supposed to be dry. You need to apply some common sense to your own situation to come up with a suitable design that will avoid such effects.

You also need to apply some of that common sense to the wet situation. Is there any residue in the fluid that might give the electrodes an insulating coating? (Greasy or oily water from a restaurant or garage could have this problem). Can the conductivity vary a lot, so the tests you performed with tap water are invalidated when the customer uses distilled water?

When you are testing electrode geometries, it is a good idea to use a multimeter to measure the resistance between the electrodes. Providing you get a reasonably steady reading for a minute or two, this is quite valid (in some fluids electro-chemical effects may make a DC resistance reading meaningless).


SPice 10200: About temperature measurement

The SPice 10200 uses a temperature measurement technique based on thermistors. This is fully documented, with calculations and a worked example, elsewhere in the SPLat Knowledge Base.

The SPice 10200 has a programmable voltage generator and selectable feed resistor value, giving you a great deal of choice for optimising your temperature measurement.


SPice 10200: Programming

Once installed on a SPLat controller, the SPice10200 appears as several regular analog and digital I/O points. The following table shows the mappings for all the I/O on the board. Numbers in parenthesis are the numeric analog channel numbers used with fAnIn and fAnOut.

 | Cnctr pin | SLXXX | MMiXXX | MS12
Conductivity 1 | 7 | Input 10 | Input 13 | Input 20
Conductivity 2 | 8 | Input 11 | Input 14 | Input 21
Thermistor 1 | 4 | AnInC (2) | AnInE (4) | AnInC (2)
Thermistor 2 | 5 | AnInD (3) | AnInF (5) | AnInD (3)
Thermistor 3 | 6 | AnInE (4) | AnInG (6) | AnInE (4)
Thermistor Vfeed | 9 | AnOutG (6) | AnOutI (8) | AnOutI (8)

Initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10200 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice10200.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10200.

         ClrU
         setu         0,3         ;set spice connector pin 4 as analog input
         setu         1,3         ;set spice connector pin 5 as analog input
         setu         2,3         ;set spice connector pin 6 as analog input
         setu         3,2         ;set spice connector pin 7 as digital input
         setu         4,2         ;set spice connector pin 8 as digital input
         setu         5,4         ;set spice connector pin 9 as analog output
         SpiceConfigU
         LoadX        XXX
         AnOutZZZ

In this code replace XXX with the Vfeed value required. You will generate this when you work through the thermistor temperature measurement tutorial. On the SPice10200 the Vfeed range is 0 to 20V (representing XXX = 0 to 255).

Note: The maximum Vfeed value you can use is limited by the supply voltage to the board. The Vfeed generator can at most get to about 2V less than the supply voltage. Hence, with say a 12V supply you can at best design for a 10V Vfeed. If you are using an unregulated supply you must take into account voltage variations and ripple. With a 24V regulated supply you will be able to use the full Vfeed range up to 20V.

Replace ZZZ with the analog output pin used for Vfeed (see table above).

Programming the fluid sensor (conductivity switch) inputs.

Once the board has been initialized, the fluid sensor inputs are just like any other input. The input will be on when there is fluid present.

Programming the thermistor inputs

Once the board has been initialized, the thermistor inputs appear as three analog inputs in the locations tabulated above. You get a raw reading from the thermistor into X using the AnIn instructions in the table above. The thermistor temperature measurement tutorial shows you how to convert the raw readings into temperatures.

After initialization there will be a delay before the Vfeed voltage settles down. Your program should not perform any control action based on a temperature reading for at least 500mS after initialization.

Note that during debug with SPLat the SPice board may not show up correctly in the I/O window unless the board has run the initialize code. You can fix this by translating and running the above initialization sequence in the board. Add a line

a     goto    a

at the end to prevent translation and runtime errors.


SPice10203 - 4 extra push buttons

sp10203 general view

Previous Topic

Next Topic

In This Chapter

SPice 10203: Introduction

SPice 10203: Installation

SPice 10203: Programming


SPice 10203: Introduction

The SPice 10203 is an add-on board for SPLat controllers that provides 4 operator push buttons. It may be used with the MMi99 to increase the number of buttons from 5 to 9, or with the SL99 to provide 4 push buttons.

The SPice10203 is a Touch of SPice board. This is the name applied to one of the several expansion systems supported within the SPLat Expansion Framework. The board may be used with MMi99/MMi200, MS12 and SL99 controllers. It does not follow that future controllers will also support it ... the Touch of SPice system is a very low cost expansion scheme that does not guarantee compatibility across the whole family of SPLat controller products.


SPice 10203: Installation

While classified as a SPice board, the SPice10203 is not mounted "piggy back" onto the SPLat controller board like most other SPice boards. Instead the board needs to be suitably mounted on the front panel of your equipment then plugged into the controller via its connection cable. We do not provide mounting panels or graphic (membrane) overlays for it. Dimensioned drawings will be provided in our online file resources and on the Resource Kit CD.

Mechanically the SPice10203 will normally be mounted behind your metal panel, with holes cut in the panel to clear the push button switches. A plastic graphics overlay is then stuck down on the outside. The concept is just like that illustrated in the MMi99 product documentation. The spacing between the board and the panel is super-critical, unless you are using a custom graphics overlay with embossed buttons. If the board is too far off the panel, it will be difficult or impossible to depress the overlay enough to activate the buttons. If the board is too close to the panel, the buttons will press permanently against the overlay and be accidentally activated. The correct spacer length is 9.5mm less the panel thickness. We use nylon spacers. These are slightly compressible, and allow some fine adjustment of the spacing by adjusting the torque on the mounting screws.

When mounting the board to the panel, make sure to be consistent with the orientation from unit to unit.

The board needs to be plugged into the 12-pin SPice connector on the controller board. On the MMiXXX you must also remove the option link labelled IN13.


SPice 10203: Programming

One installed on a SPLat controller, The SPice10203 appears as several regular digital inputs. When viewed from the button side, with the writing on the board the right way up, the 4 buttons are numbered from right to left (backwards). The following table shows the mappings for all the buttons:

 | Cnctr pin | SL99 | MMi99; MMi200 | MS12
Button 1 | 7 | Input 10 | Input 13# | Input 20
Button 2 | 6 | Input 14* | Input 17* | Input 19
Button 3 | 5 | Input 13* | Input 16* | Input 18
Button 4 | 4 | Input 12* | Input 15* | Input 17

# Input 13 on the MMi99 doubles duty as one of the option links. When the SPice 10203 is used, the option link is not useable and must be physically removed from the board.

* These SPice pins are by default not digital inputs. They only become inputs after they have been initialized by a running SPLatWare program (see below). Because of this, you will not be able to see them respond on your computer screen when the board is connected to SPLat/PC.

initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10203 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice10203.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10203.

         ClrU
         setu         0,2         ;set spice connector pin 4 as digital input
         setu         1,2         ;set spice connector pin 5 as digital input
         setu         2,2         ;set spice connector pin 6 as digital input
         setu         3,2         ;set spice connector pin 7 as digital input
         SpiceConfigU

Program example

;Demonstration/Test of the SPice 10203
;This program works on an SL99/MMi99. The extra buttons are
;reflected on the outputs 0 to 3

;Use one or the other of the following sets of definition

;depending on which board you are using ...

Button1  iEQU         13         ;Definition for MMi99

Button2  iEQU         17         ;Definition for MMi99

Button3  iEQU         16         ;Definition for MMi99

Button4  iEQU         15         ;Definition for MMi99

;Button1  iEQU         10         ;Definition for SL99

;Button2  iEQU         14         ;Definition for SL99

;Button3  iEQU         13         ;Definition for SL99

;Button4  iEQU         12         ;Definition for SL99

Start:

         ClrU

         setu         0,2         ;set spice connector pin 4 as digital input

         setu         1,2         ;set spice connector pin 5 as digital input

         setu         2,2         ;set spice connector pin 6 as digital input

         setu         3,2         ;set spice connector pin 7 as digital input

         SpiceConfigU

Loop:

         Input        Button1

         Output       0

         Input        Button2

         Output       1

         Input        Button3

         Output       2

         Input        Button4

         Output       3

         GoTo         Loop

Note that during debug with SPLat the SPice board may not show up correctly in the I/O window unless the board has run the initialize code. You can fix this by translating and running the above initialization sequence in the board. Add a line

a     goto    a

at the end to prevent translation and runtime errors.


SPice10204 - Current transformer

Previous Topic

Next Topic

In This Chapter

SPice 10204: A quick tour

SPice 10204: Safety Warning

SPice 10204: Fitting to controller

SPice 10204: Power supply requirements

SPice 10204: Current sensing wire and ranging

SPice 10204: Preset potentiometers

SPice 10204: Programming

SPice 10204: Accuracy and calibration considerations


SPice 10204: A quick tour

The picture above shows the SPice 10204 mounted on an MMi99, with the major parts called out. These are:

  • The current transformer (CT) component. This consists of a toroidal core that has several thousand turns of wire wound around it. That coil forms the secondary winding of a transformer.
  • The current wire. You must provide this. It forms the primary winding of the current transformer.
  • Potentiometers 1 and 2. These are available for general purpose use, and have nothing to do with the CT. The potentiometer settings can be read by your program.


SPice 10204: Safety Warning

It is your responsibility to ensure that the current carrying wire you feed through the current transformer on the SPice 10204 is insulated in accordance with your local regulations. In particular, if it is part of a mains (or higher voltage) circuit it may carry dangerous voltage levels. While the CT itself is insulated to 2500VAC (not a particularly high spec relative to European standards!), there is no such insulation on the rest of the board. Part of your safety precautions should therefore include anchoring the current wire in such a way that it can never touch any part of the SPLat components other than the CT itself.

If you have any doubts about this use an external CT as an isolation barrier.

The SPice 10204 is sold as a component, for inclusion in an end product or system in accordance with all relevant local regulations and standards. In using the SPice 10204 you are assuming full responsibility for compliance with such regulations and standards. If this is not acceptable to you please return the SPice 10204 to the supplier for a full refund of the purchase price.


SPice 10204: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  2. Identify the matching single in-line pin connector on the controller board.
  3. Experimentally plug the two boards together, taking care to align the connectors
  4. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  5. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  6. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.
  7. See safety warning


SPice 10204: Power supply requirements

The SPice 10204 is designed to operate with a 24VDC power supply. It draws its power from the host SPLat controller, so when you are using the SPice 10204 you will have to run the controller off a 24V regulated supply.


SPice 10204: Current sensing wire and ranging

A wire carrying the AC current to be measured must be passed through the hole in the large, black current transformer component. For safety reasons always use an insulated wire. See safety warning.

The SPice 10204 is factory calibrated for 5A full scale. We calibrate using the mains power supply and a true rms meter. The circuit is average reading.

If you want a lower current full scale you can pass the current carrying wire through the transformer hole more than once. For example, if you wind the wire around the transformer so it passes 5 times through the hole, you will get 1A full scale. Don't overlook safety insulation requirements if you do this.

If you want a higher current scale, you can use an external current transformer as a "front end". Most commercial current transformers sold for electrical use have full scale output currents of 1A or 5A. Using an external scaling transformer has the added advantage that it becomes the primary safety isolation barrier (providing you use a device with the appropriate approvals).


SPice 10204: Preset potentiometers

The SPice 10204 board contains two small potentiometers. These may be used for any arbitrary purpose in your programming, for example for generating scale factors, calibration numbers or perhaps adjustable timers. How you make use of the potentiometer readings is entirely up to your needs and ingenuity.

The potentiometers appear to your program as two analog inputs, and when read in will give a number from 0 to 255 depending on the setting of the potentiometer. Note that due to inevitable errors it may be that 1 or 2 values at either end never occur, so don't use the use the readings in such a way that your system would fail if it doesn't get a reading <3 or >252.


SPice 10204: Programming

Once installed on a SPLat controller, the SPice10204 appears as 3 regular analog input points. The following table shows the mappings for the inputs on the board. Numbers in parenthesis are the numeric analog channel numbers used with fAnIn.

 | Cnctr pin | SLXXX | MMiXXX | MS12
Current transformer | 4 | AnInC (2) | AnInE (4) | AnInC (2)
Potentiometer 1 | 5 | AnInD (3) | AnInF (5) | AnInD (3)
Potentiometer 2 | 6 | AnInE (4) | AnInG (6) | AnInE (4)

initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10204 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice10204.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10204.

         ClrU
         setu         0,3         ;set spice connector pin 4 as analog input
         setu         1,3         ;set spice connector pin 5 as analog input
         setu         2,3         ;set spice connector pin 6 as analog input
         SpiceConfigU

Programming the current transformer

The current transformer appears as analog input C on an SL99 and E on an MMi99. It has a full scale range of 0 to 5A. The following MMi99 program will display the current on the LCD (if fitted)

         ClrU                     ;clear U
         setu         0,3         ;set spice connector pin 7 as analog input
         setu         1,3         ;set spice connector pin 8 as analog input
         setu         2,3         ;set spice connector pin 9 as analog input
         SpiceConfigU

loop

         oblcd_setcur 0,0         ;Position the LCD cursor

         oblcd_text   "I="        ;Show a heading

         AnInE                    ;Read the CT

         Float                    ;Convert to floating point

         fLoadQ       1.960784E-2 ;Scale factor for 5A FS

         fMul

         OBLCD_fDispW 4,2         ;Display

         GoTo     Loop            ;Repeat forever

Programming the potentiometers

There are a myriad of possible uses for the potentiometers. Once the board has been initialized, the actual act of reading a potentiometer is a simple matter of executing an AnIn instruction. The following example shows how you could perform a SPLatWare calibration of the current transformer. In this case, in an MMi99/200, potentiometer 1 is used to provide ±5% adjustment of the CT reading. This involves generating a calibration factor of between 0.95 (-5%) and 1.05 (+5%) for potentiometer readings between 5 and 250 (allowing for a few possible missing codes either end). The mid-point reading of the potentiometer is 127.

         ClrU                     ;clear U

         setu         0,3         ;set spice connector pin 7 as analog input

         setu         1,3         ;set spice connector pin 8 as analog input

         setu         2,3         ;set spice connector pin 9 as analog input

         SpiceConfigU

loop

         AnInF                    ;Read potentiometer 1

         Float

         fLoadQ       -127        ;Mid-point

         fAdd                     ;Now scaled -127 to +128

         fLoadQ       4.1E-4      ; = 0.05/123

         fMul                     ;now scaled -0.05 to +0.05

         fLoadQ       1

         fAdd                     ;now have 0.95 to 1.05 = cal factor

         fLoadQ       1.960784E-2 ;Scale factor for 5A FS

         fMul                     ;Composite scale and calibration factors

         fSwap                    ;Save it away in Q

         oblcd_setcur 0,0         ;Position the LCD cursor

         oblcd_text   "I="        ;Show a heading

         AnInE                    ;Read the CT

         Float                    ;Convert to floating point

         fMul                     ;The scale/cal factor already in Q

         OBLCD_fDispW 4,2         ;Display

         GoTo     Loop            ;Repeat forever

Note: For experimentation, if you don't have a 5A AC current source handy, change the AnInE to AnInG, and use potentiometer 2 in lieu of the CT.

Note that during debug with SPLat the SPice board may not show up correctly in the I/O window unless the board has run the initialize code. You can fix this by translating and running the above initialization sequence in the board. Add a line

a     goto    a

at the end to prevent translation and runtime errors.


SPice 10204: Accuracy and calibration considerations

The output of the current transformer on the SPice 10204 is processed by onboard circuitry to produce a DC voltage that is measured by the controller's analog to digital converter. The processing consists of:

  • An AC pre-amplifier that amplifies the small signal from the CT to a useable level.
  • A precision rectifier that uses an operational amplifier and diodes to produce a fullwave rectified representation of the ac current waveform.
  • A dc averaging circuit (filter) that converts the fullwave signal into a smooth DC.
  • A calibration potentiometer (quite different to the user accessible general purpose potentiometers).

During factory calibration we apply 4.902A rms at 50Hz to the CT and adjust the calibration potentiometer for 250 counts. We do it at just under full scale to guard against over-ranging the analog input on the controller.

There are several sources of potential inaccuracy in this chain:

  1. The accuracy of our 4.902A test current.
  2. Non-linearity and offset errors in the precision rectifier at currents significantly less than full scale.
  3. The calibration accuracy of the host controller board.

Overall, the above factors may add up to about ±1% to 2% (of full scale) with currents over 5% of full scale. We don't and won't quantify these, because they pale into significance in relation to the one factor we, or you, cannot control namely ...

Effect of non-sinusoidal current

The SPice 10204 uses a simple averaging measurement. It is calibrated for a sinewave. For any waveform other than sinusoidal it will be inaccurate. Non-sinusoidal waveforms can be due to something as simple as harmonic distortion in the mains voltage caused by non-linear loads such as fluorescent lighting. To make matters worse, many mains loads draw very non-sinusoidal currents even if the voltage is a perfect sinewave (fluorescent lighting being a classical example).

This, by the way, is no different to a typical multimeter. Some up-market multimeters boast true rms AC measurement, but most use pretty much the same kind of average reading circuitry as the SPice 10204.

The result of all this is that any program you write needs to either be adjusted for whatever non-sinusoidal waveform you encounter, or should use a strategy that establishes a practical norm and then works from there. Any such strategy should include a margin of error for changes in the mains distortion.

Effect of frequency

The SPice10204 is designed and suitable only for 50Hz and 60Hz currents. Onboard lowpass filtering will impair accuracy at higher frequencies.


SPice10205 - 6 fluid level switches

Previous Topic

Next Topic

In This Chapter

SPice 10205: Introduction

SPice 10205: A quick tour

SPice 10205: Fitting to controller

SPice 10205: Power supply requirements

SPice 10205: External connections

SPice 10205: About water level detection

SPice 10205: Programming


SPice 10205: Introduction

The SPice 10205 is an add-on board for SPLat controllers that provides six channels of fluid level switching.

The fluid level sensing works on the principle of detecting an electric current flow between two electrodes submersed in the fluid. Hence, it works on electrically conductive fluids such as water, milk, detergent etc. It will not work with non-conductive fluids like ultra-pure water or oils.

The SPice10205 is a Touch of SPice board. This is the name applied to one of the several expansion systems supported within the SPLat Expansion Framework. The board may be used with MMi99, SL99 and MS12 controllers. It does not follow that future controllers will also support it ... the Touch of SPice system is a very low cost expansion scheme that does not guarantee compatibility across the whole family of SPLat controller products.


SPice 10205: A quick tour

The above picture shows the SPice10205 with the main items of interest labeled.

The two connectors in the middle of the board are for connecting your electrodes. There are two pins per channel, one for the active electrode (numbered with the channel number) and one for a ground (0V) connection. All 0V pins are connected together on the board and to the 0V power supply pin on the host controller board. The electrodes, and the use of the 0V connections, are explained in SPice 10205: About water level detection

The component labeled "V" is the sensitivity adjustment. It controls the sensitivity on all channels at the same time.

The item marked "L" is a link that enables/disables channel 6. It is shown in the disabled position. To enable channel 6 it must be installed so it bridges the two gold pins. The reason for the link is that conductivity channel 6 uses the same pin on the controller board's processor chip that is used for generating a thermistor drive voltage on the main board (SL99 or MMi99, this does not apply to MS12). If you are using the main board's analog input in thermistor mode, then you cannot use conductivity channel 6 on the SPice10205, and the link must be removed.


SPice 10205: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. If you are using an MMi controller, remove the two option links
  2. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  3. Identify the matching single in-line pin connector on the controller board.
  4. Experimentally plug the two boards together, taking care to align the connectors
  5. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  6. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  7. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice 10205: Power supply requirements

The board draws its power from the host controller board. It consumes less than 5mA from the 5V supply.


SPice 10205: External connections

The SPice 10205 has two connectors for external connections.

The Developer's kit version of the board comes with a set of matching connector housings and contacts. If you are buying your boards in the OEM version, you will need to obtain matching connectors separately. In a volume OEM situation you would most likely have a wiring harness pre-made by a specialist contractor, ready to just plug onto the board and attached devices.

The large, white, single pin-row connectors on the board are commonly referred to as "KK". They are 0.156" or 3.96mm pitch. Matching female connectors are available from a number of manufacturers. They are available in two principle styles:

  • Insulation Displacement Connectors (IDC). In this style the contact and plastic body come as one part, and the wire is installed with a special tool that forces points on the contact to penetrate the wire insulation and make contact with the metallic conductors. These are suitable for small volume production.
  • Separate plastic housings and crimp contacts.You need to order the plastic connector housing and the metal contacts separately. The contacts come either in continuous strips for machine crimping or in singles for manual crimping. This type of connector is best suited to higher volume production.

If you use the IDC types you will also need a suitable crimping tool. Please ask your connector supplier for a suitable type. We recommend a good quality ratchet tool and proper operator training. The most common cause of connection failure is improper crimping.

Note, too, that the contacts are designed to handle a defined range of wire gauges. Take care to use the correct gauge of wire, or you will have problems.

The following table lists a number of connector manufacturers. In the housing part number ??? represents the number of poles (contact positions). Fill unused ? positions with 0's, i.e. for 4-way "???" becomes "004"

IDC types

Manufacturer | 24AWG wire | 22AWG wire | 20AWG wire
AMP | 640429-? | 640422-? | 640426-?
Molex |  | 09-06-???? |

Crimp terminal types

Manufacturer | Housing part # | Contact part #
Oupiin | 4771-??H | 4771-Pin-T
Methode | 3360-??? | 3400-112
Molex | 09-91-??00 | 08-50-0106

If you are in Australia, you can conveniently buy small quantities of connectors from Altronics Pty Ltd (Phone 1300-797-007. Altronics part numbers are P5640 plus xx, where xx is the number of ways. For example, a 10-way connector is P5650. These come with matching crimp terminals. Altronics also sell a pack of 100 crimp pins: p/n P5640.

Digikey sells the parts in the US. In Europe and many other places you can get them from Farnell. The following list gives the Farnell order codes versus number of ways:

2 ways:143-157; 3 ways:143-158; 4 ways:143-159; 5 ways:257-485; 6 ways:143-160; 8 ways:143-161; 10 ways:143-522; 12 ways:257-497 The Farnell order number for contacts is 143-193 for 100 terminals and 143-198 for 1000 terminals.


SPice 10205: About water level detection

The SPice 10205 has six channels of water level switching. These work on the principle that if two electrodes have a voltage applied between them, and they are submersed in an electrically conductive fluid, a detectable current will flow. The amount of current that flows is proportional to the surface area of the electrodes (larger electrodes = more current) and inversely proportional to the distance between the electrodes (more spacing = less current). The current also depends on the conductivity of the fluid (very pure water = small current, very salty water = much more current). For a given electrode geometry and fluid the overall conduction can be defined as a resistance value.

The conductivity circuit on the SPice 10205 is designed to switch on and off at a certain resistance. The board has an adjustment for setting this resistance sensitivity. The adjustment range is approximately 2.2K to 27K. The circuit applies an AC voltage to the electrodes (DC can cause electrolysis problems).

Each of the six conductivity channels has a 2-wire connection, for 2 electrodes. On each channel one electrode is connected on the board to 0V or ground, while the other electrode is the "active" one. If all channels are used in a single tank to indicate up to 6 levels, then only one ground electrode is required. If you are monitoring several isolated (not interconnected) tanks, then each will require its own ground electrode.

It is important to realise that the grounds are all connected together and therefore at the same potential. The SPice10205 senses the resistance between each active electrode and ground. If you were to try and have multiple ground electrodes all within the one fluid mass (say several interconnected tanks), the board could get confused. It can also get very confused if you have any other device in the fluid that generates AC or DC voltages or currents.

You have to design and provide your own electrodes. In the main this requires mainly common sense plus a bit of experimentation.

There are two main classes of electrode design, which are governed by the nature of the vessel or pipework in which you want to detect the presence or absence of fluid. If the vessel is made of an electrically insulating material you will need two electrodes. If the vessel is made of metal, you will very possibly be able to use the vessel itself as one electrode. In the latter case you have to make sure the vessel is connected to the 0V (ground) electrode connection because the vessel will usually already be at ground potential.

It is usually advisable to use stainless steel for the electrodes. In many cases something as simple as two stainless steel machine screws through the side of a plastic vessel will do, with wires connected on the outside. In a metal vessel, a single screw with an insulated mounting arrangement, plus a wire from the screw and one from the body of the vessel (don't rely on ground conduction for the return path).

There are two parameters that affect the performance of your electrodes: How much they conduct when they are in fluid (wet) and how little they conduct when they are not in the fluid (dry).

How could dry electrodes conduct? Because they may not be dry! Wetting, residue build-up, even solid matter or foam, can all form unexpected and unwanted conduction paths when the electrodes are supposed to be dry. You need to apply some common sense to your own situation to come up with a suitable design that will avoid such effects.

You also need to apply some of that common sense to the wet situation. Is there any residue in the fluid that might give the electrodes an insulating coating? (Greasy or oily water from a restaurant or garage could have this problem). Can the conductivity vary a lot, so the tests you performed with tap water are invalidated when the customer uses distilled water?

When you are testing electrode geometries, it is a good idea to use a multimeter to measure the resistance between the electrodes. Providing you get a reasonably steady reading for a minute or two, this is quite valid (in some fluids electro-chemical effects may make a DC resistance reading meaningless).


SPice 10205: Programming

Once installed on a SPLat controller, the SPice10205 appears as several regular analog and digital I/O points. The following table shows the mappings for all the I/O on the board.

 | Cnctr pin | SL99 | MMi99; MMi200 | MS12
Conductivity 1 | 7 | Input 10 | Input 13 | Input 20
Conductivity 2 | 8 | Input 11 | Input 14 | Input 21
Conductivity 3 | 4 | Input 12 | Input 15 | Input 17
Conductivity 4 | 5 | Input 13 | Input 16 | Input 18
Conductivity 5 | 6 | Input 14 | Input 17 | Input 19
Conductivity 6 | 9 | Input 15 | Input 18 | Input 22

initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10205 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice10205.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10205.

Use this code if you are using all 6 channels for conductivity

         ClrU
         setu         0,2         ;set spice connector pin 4 as digital input
         setu         1,2         ;set spice connector pin 5 as digital input
         setu         2,2         ;set spice connector pin 6 as digital input
         setu         3,2         ;set spice connector pin 7 as digital input
         setu         4,2         ;set spice connector pin 8 as digital input
         setu         5,2         ;set spice connector pin 9 as digital input
         SpiceConfigU

If you are using the thermistor drive voltage generator, channels 6 of the SPice10205 cannot be used. Instead you need to remove the jumper on the SPice10205 board and use the following initialization code:

         ClrU
         setu         0,2         ;set spice connector pin 4 as digital input
         setu         1,2         ;set spice connector pin 5 as digital input
         setu         2,2         ;set spice connector pin 6 as digital input
         setu         3,2         ;set spice connector pin 7 as digital input
         setu         4,2         ;set spice connector pin 8 as digital input
         setu         5,4         ;set spice connector pin 9 as analog output
         SpiceConfigU

Programming the fluid sensor (conductivity switch) inputs.

Once the board has been initialized, the fluid sensor inputs are just like any other input. The input will be on when there is fluid present.

Note that during debug with SPLat the SPice board may not show up correctly in the I/O window unless the board has run the initialize code. You can fix this by translating and running the above initialization sequence in the board. Add a line

a     goto    a

at the end to prevent translation and runtime errors.


SPice10206 - 3rd party embedded web server support

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

The SPice10206 board is a Tiny Serial Peripheral that interfaces a 3rd-party embedded webserver product to SPLat. The webserver product is SitePlayer Server SP1 from NetMedia. SitePlayer provides a 10MHz Ethernet CAT5 (RJ45) connection, meaning it can be connected to the vast majority of Local Area Networks (LANs) in the world.

It is important that you are aware of the distinction between the SPice10206 and the SitePlayer module. We do not make or support the SitePlayer. We normally do not sell you the SitePlayer either, though we may supply them as part of a special arrangement. Our contribution is to supply and support the SPice10206 board that enables the SPLat to communicate with the SitePlayer. Every SPice10206 board sold is 100% tested with a SitePlayer.

For development purposes we strongly recommend that you purchase a SitePlayer development kit from NetMedia, in addition to the SPice10206 you buy from us. You should also buy your SitePlayer SP1 modules directly from NetMedia. Be sure to order the SitePlayer Server SP1, not the SitePlayer Telnet (SPT1).

We selected the SitePlayer because it is a very effective embedded webserver design with a very simply and easily understood interface to the host processor (i.e. the SPLat). The SitePlayer metaphor of SiteObjects, and the SitePlayer host communications protocol, were in fact the inspiration for our Tiny Serial Peripheral interface.

Note: This is a TSP (Tiny Serial Peripheral). It is compatible only with selected SPLat Controllers. Please check the controller documentation to see if it is TSP compatible. That may mean checking its firmware revision history against the Firmware you have in your board(s)

Previous Topic

Next Topic

In This Chapter

SPice10206: Getting started

SPice10206: What you can achieve with SitePlayer Server

SPice10206: Fitting to controller

SPice10206: Power supply requirements

SPice10206: Programming


SPice10206: Getting started

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

You will find that getting an application going with the SPice10206/SitePlayer combination is straightforward, providing you adopt the roadmap set out in the TSP overview and backgrounder document. In particular, you need to get a very firm grasp on the Internet related aspects of the SitePlayer, such as:

  • Web page design and creation.
  • SitePlayer's system of configuration files and data files
  • IP addressing schemes, port numbers and MAC addresses.
  • Configuring routers, switches, firewalls and broadband modems.

These are all matters covered in the SitePlayer documentation, which you can download from the SitePlayer website, and elsewhere. SPLat Controls cannot provide support in these matters. We strongly recommend that you make yourself thoroughly familiar with the SitePlayer in its own developer kit before attempting to use it with the SPLat. Software provided with the SitePlayer developer kit includes utilities for setting its IP address, communicating with it over your LAN and all manner of other useful things.

Do not perform TSP transfers to the SitePlayer during Ethernet downloading of new firmware, or web pages. If you must perform this action in the field, you need to have some way of ensuring that TSP activity stops during the updates. This could include providing a special mode in the SPLat that does no TSP transfers, loading a null program into the SPLat or transferring the SitePlayer module to a SitePlayer developer kit during the download.


SPice10206: What you can achieve with SitePlayer Server

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

This section outlines briefly the kinds of things you can achieve with the SitePlayer Server when interfaced to a SPLat. This is not a "how to" manual. You need a thorough understanding of the SitePlayer and its documentation to learn how to do these things. This section is intended to let you quickly find out what can be achieved and to point you in the right direction.

In This Section

SPice10206: Anywhere on the Web

SPice10206: Simple hardware I/O

SPice10206: SitePlayer Server and web pages

SPice10206: UDP messages

SPice10206: Generating email

SPice10206: Cellphones


SPice10206: Anywhere on the Web

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

The SPice10206 and SitePlayer combination make it possible for you to create SPLat applications that are accessible from anywhere on the world wide web using just a web browser such as Internet Explorer or Mozilla. A very large percentage of business and industrial premises these days have a local area network (LAN) based on Ethernet and CAT5 cabling, and usually the LAN is connected to the Internet via a permanent broadband connection. That means there is a very good chance that the required communications infrastructure is in place.


SPice10206: Simple hardware I/O

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

The SitePlayer Server has eight hardware I/O bits that can be referenced and/or controlled from web pages. Four of these are connected to the SPLat SPice connector on the SPice10206. That means for the very simplest applications, where only up to 4 bits of control are needed, you may be able to get away with not even using the TSP communication interface.

The four bits are designated IO0 through IO3 in SitePlayer. You can find reference to them in the SitePlayer software manual under "Special functions memory map".These pins are connected to SPice connector pins 6, 7, 8 and 9 respectively. Which I/Os these map to on the SPLat controller depends on the controller. You should refer to the section "SPice connector" within the documentation for your specific SPLat controller, for that mapping. The following table-cum-worksheet should assist. It has been filled in for all applicable controllers at time of writing:

Site-Player | SPice cnctr | SL99 |  | MMi99/200 |  | My SPLat
 |  | In | Out | In | Out | In | Out | My function
IO0 | 6 | 14 | 10 | 17 | 18 |  |  |
IO1 | 7 | 10+ | 11 | 13+ | 19 |  |  |
IO2 | 8 | 11+ | 12 | 14+ | 20 |  |  |
IO3 | 9* | 15 | 13 | 18 | 21 |  |  |

+ Inverted logic

* Doubles as thermistor drive voltage (analog output). If you are using a thermistor on the main board you cannot use this pin on the SPice10206, and you must remove the jumper labeled LK1.

Within the SitePlayer, each bit consists of an output and an input connected to a single pin, very much like SPLat bidirectional I/O points.

Displaying bits on a web page

On the SitePlayer pins IO0 through IO3, if the pin is set to a '1' by the SitePlayer, it can be used as an input to the SitePlayer. In this case the corresponding SPice pin should be configured as an output. With suitable references in your html file, the state of that SPLat output can be displayed on a web page.

Controlling input bits to the SPLat

If a SPice pin is set as an input (to the SPLat), then the SitePlayer can turn that pin on and off in response to a suitably referenced link or other control on a web page. Note that the pins flagged above with a + sign have inverted logic. These are the pins that double up as configuration jumpers on the MMi99 and MMi200 main board (the jumpers must be removed when using SPice boards!). On these pins a 0V input signal is taken as an ON state.


SPice10206: SitePlayer Server and web pages

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

The basic model of the SitePlayer Server is this:

You write yourself one of more web pages in html. Within those pages you make reference to "SiteObjects". SiteObjects are memory variables held in the SitePlayer. When a surfer comes along and accesses your page, the SitePlayer serves up the page with each SiteObject reference replaced by the actual memory value.

For example, your html file may contain the following:

<P>The temperature is ^temp degrees C</P>

Meanwhile, if the SPLat program has loaded the number 25 into the SitePlayer RAM location corresponding to the variable named temp, then when the above html code is served up it will read

The temperature is 25 degrees C

Similarly (though a bit more complicated!), input data can be collected from a web page via links with embedded parameters or the form GET method. This is documented in the SitePlayer software manual.


SPice10206: UDP messages

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

UDP stands for User Datagram Protocol. UDP allows arbitrary messages to be encapsulated within a data packet and sent to any computer in the world that is connected to the Internet, whose address is known, and which is running suitable software that can make sense of it. What this means is that UDP allows a SPLat fitted with a SitePlayer to initiate a data transfer. Web pages, by comparison, must be "pulled", i.e. a remote computer can only receive a web page by asking for it. UDP messages can be "pushed", i.e. sent without any prompting. That makes them more suitable for "reporting by exception" such as you would use for alarms or emergencies.

It is important to note the "running suitable software" condition in the above paragraph. To put together a UDP based system you will need to have suitable software in the destination computer. You will also need to generate the appropriate UDP messages in the SitePlayer (please refer to the SitePlayer software manual).

The SPLat TSP instruction set supports UDP with an instruction called TSPSendUV.


SPice10206: Generating email

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

The SitePlayer website includes an example Visual Basic application that is capable of receiving a UDP message and generating an email. So while the SitePlayer on its own can't generate emails, you can certainly set up a private email gateway.


SPice10206: Cellphones

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

With a web-enabled cellphone it is perfectly possible to access a SitePlayer and control a SPLat (we've done it). It is also possible to get a SitePlayer to instigate an email, and email to SMS gateways exist (some US cellphones come with an email address!).


SPice10206: Fitting to controller

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  2. Identify the matching single in-line pin connector on the controller board.
  3. Experimentally plug the two boards together, taking care to align the connectors
  4. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  5. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  6. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.
  7. Fit a SitePlayer module. Take great care to line up the orientation dots on the SitePlayer and the SPice10206.
  8. If you are using thermistors on the analog inputs to the SPLat controller, remove the jumper labeled LK1 on the SPice10206 board. See also SPice10206: Simple hardware I/O for clarification.


SPice10206: Power supply requirements

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

The SPice10206 with SitePlayer fitted draws its power from the SPLat controller board. It draws approximately 80mA. The SPLat must be powered off a 12 to 24V supply. Note that in this configuration the supply voltage must not exceed 24V. We recommend a regulated power supply.


SPice10206: Programming

The SPice10206 is no longer on sale for new designs. Please contact us if you have an existing design that uses this product.

Programming the SPice10206 and SitePlayer requires mainly an understanding of the SPLat Tiny Serial Peripheral (TSP) instructions and of how SiteObjects in the SitePlayer work and correspond to certain addresses. Your first port of call should be the SitePlayer documentation. We strongly recommend that you buy a SitePlayer developer kit, connect it to your PC and become thoroughly familiar with using and programming it via the PC.

There is also a Yahoo discussion group for SitePlayer which is a forum where you can exchange ideas with other SitePlayer users, and sometimes get help.

Once you have mastered the SitePlayer itself you will find that extending what you have learned to the SPLat and the TSP instructions is quite straightforward.

The TSP instructions allow you to write bits, bytes, words, long data ("Doubles" in SitePlayer parlance) and strings of up to 16 bytes to the SitePlayer. You can also interrogate the SitePlayer for the same data types. Data transfers go via the U register in SPLat.

The only real complication is that when you request a data item from the SitePlayer, it is not available instantly. For this reason we have a TSPBranch instruction that allows you to test if the requested data is yet available from the SitePlayer. You may find our multitasking tutorial useful to discover ways of making your application continue doing other things while a response is pending from the SitePlayer.

In the SitePlayer software manual you will find definitions of SitePlayer serial commands. These are the messages the SitePlayer can receive through its serial port (RS232 on the SitePlayer developer kit) and respond to. SitePlayer serial commands are mapped to SPLat TSP instructions according to the following table:

TSP

SP

Comments

TSPGetBit

ReadBit

TSPPutBit

WriteBit

TSPGetByte

TSPGetWord

TSPGetLong

TSPGet$

ReadX

Each of these TSP instructions actually comes in two versions, a "low" and a "high", denoted by an "L" or "H" tacked on the end on the instruction. The "low" versions address the first 32K of address space, the "high" versions address the top 32K.

Few TSP devises are likely to have more than 32K of memory, so normally you will be using the "L" version.

TSPPutByte

TSPPutWord

TSPPutLong

TSPPut$

WriteX

TSPGetStatus

Status

TSPSendUV

Depends entirely on what you send. Typically used to generate a UDPSend


SPice10209 - Type K thermocouple interface

This product is discontinued. It has been replace by the SX10500 Xwire peripheral board

The SPice10209 is an add-on board that provides a SPLat controller with one or two channels of K-type thermocouple input. It uses the Maxim MAX6675 chip to digitize the thermocouple reading. The MAX6675 data is in the "File Resources" area of our resources CD (after October 2004) and also available online. The measurement attributes, including accuracy, are governed by that chip. The salient features are:

  • Works with K-type thermocouples;
  • Temperature range 0°C to 1024°C is 0.25°C steps (resolution);
  • built in cold-junction compensation;
  • Accuracy depends on temperature - see chip data.

On the SPice10209 one or two MAX6675 chips are interfaced to the SPLat controller via a TSP (Tiny Serial Peripheral) interface. A dedicated processor provides the interface function.

The MAX6675 takes a fresh temperature reading every 250mS. The SPice10209 processor chip provides flags that allow you to detect when a new reading is available. This can help you avoid processing the same reading more than once (which may or may not be significant, depending on how busy or sophisticated your program is). The flags are available either as simple digital inputs via the SPice pins, or as bit variables via the TSP interface.

There is also an error flag for each channel. This indicates an open thermocouple input, or in the case of the single channel version, an absent channel. The two error flags are available as individual bit variables (via TSP) or multiplexed into a single SPice pin. In the latter case the SPice pins will indicate the flag for the channel which was last read out.

The onboard processor also provides the service of optionally low pass filtering readings to reduce noise. The filter time constant can be set between 0.5S and 4S.

To make the most of your SPice10209 temperature measurements you should read up on thermocouple theory and precautions for their use.

Note: This is a TSP (Tiny Serial Peripheral). It is compatible only with selected SPLat Controllers. Please check the controller documentation to see if it is TSP compatible. That may mean checking its Firmware revision history against the firmware you have in your board(s).

Previous Topic

Next Topic

In This Section

SPice10209: Fitting to controller

SPice10209: Power supply requirements

SPice10209: Thermocouple connections

SPice10209: Memory map

SPice10209: SPice pins

SPice10209: Programming


SPice10209: Fitting to controller

This product is discontinued. It has been replace by the SX10500 Xwire peripheral board

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. If you are using an MMi controller, remove the two option links
  2. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  3. Identify the matching single in-line pin connector on the controller board.
  4. Experimentally plug the two boards together, taking care to align the connectors
  5. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  6. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  7. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice10209: Power supply requirements

This product is discontinued. It has been replace by the SX10500 Xwire peripheral board

The SPice10209 draws its power from the host SPLat board. It draws about 25mA maximum, which appears as that much additional current draw by the controller. It can be used over the full 10V to 28V supply range.


SPice10209: Thermocouple connections

The thermocouple is connected via simple screw terminal blocks.

Do not attempt to connect to the 3-pin plug labeled ICP. It is strictly only for our use during manufacture.


SPice10209: Memory map

This product is discontinued. It has been replace by the SX10500 Xwire peripheral board

The following table summarizes where various data are located in the memory of the SPice10209 processor.

Address | Contains | Access with
0 & 1 | Channel 1 raw reading | TSPGetWordL 0
2 & 3 | Channel 2 raw reading | TSPGetWordL 2
4 & 5 | Channel 1 filtered rdg | TSPGetWordL 4
6 & 7 | Channel 2 filtered rdg | TSPGetWordL 6
8 | Filter time constant select | TSPPutByte 8
9 | Status bits | See below

The status bits are as follows:

Bit | Meaning | Access with
0 | New data available on channel 1. | TSPGetBit 0
1 | New data available on channel 2. | TSPGetBit 1
2 | Channel 1 fault | TSPGetBit 2
3 | Channel 2 fault | TSPGetBit 3

The status bits are also accessible via SPice pins, which is usually the easiest way to access them.


SPice10209: SPice pins

This product is discontinued. It has been replace by the SX10500 Xwire peripheral board

Some status data is available from the SPice10209 on SPice pins. This is the same information as in the status bits given in the memory map, only accessed in a different way (by regular Input or GoIfInOn/Off instructions). Because these are SPice pins they will map to different input numbers depending on which SPLat controller is hosting the SPice10209. The following table-cum-worksheet shows the input numbers for each of the compatible SPLat controllers existing at time of writing.

 | SPice pin | SL99 | MMi99/200 | My; SPLat | U
 |  | Input number
Fault | 6 | 14 | 17 |  | 2
Ch 1 rdg | 7 | 10 | 13 |  | 3
Ch 2 rdg | 8 | 11 | 14 |  | 4

+ Inverted logic

The 'U' column indicates which byte of U needs to be set prior to a SPiceConfigU instruction. Those bytes of U should be set to 2 (inputs) for the SPice10209.

SPice pins 4 and 5 (not shown above) are used for TSP communications. SPice pin 9 may used on MMi99/200 and SL99 as a programmable voltage source for thermistors. The SPice10209 does not use pin 9.

The Fault pin (SPice pin 6) is shared between the two channels. It will give the correct indication for the channel that was last read out. Hence to use the Fault pin you must first read the channel data (reading either raw, unfiltered data or the filtered data will do the trick).

The SPice pins simply provide an alternative way of access status bits that are also available via the TSP interface. They do have the advantage of being simpler to access than the status bits.


SPice10209: Programming

This product is discontinued. It has been replace by the SX10500 Xwire peripheral board

As with any TSP peripheral, it is important that you keep clear in your mind the difference between the TSP interface and the peripheral function itself. In the case of the SPice10209 the peripheral function consists of one or two thermocouple input devices (MAX6675 chips) while the TSP interface consists of an onboard processor in the SPice10209 and a firmware driver in the SPLat controller. The MAX6675 has one set of needs, wants and attributes, and the TSP interface has a different set.

MAX6675

The MAX6675 chip generates a fresh temperature reading every 250mS. While it is possible to extract readings as fast as you can, you will only get a new reading every 250mS. The MAX6675 also generates a flag to advise when a new reading is available and can generate a fault flag is the thermocouple terminals are open circuit.

TSP processor

The TSP processor must communicate with the SPLat processor using a set protocol. In particular, the SPLat UV register pair is used to pass information back and forth. Because UV is used for other things like communicating with the OnBoard LCD (OBLCD), there are programming mechanisms that allow you to prevent conflicts. The programming of the TSP interface (communications) has its own documentation. You need to understand that material, in particular the significance of the TSPBranch instruction.

Previous Topic

Next Topic

In This Section

Example: Simple read and display using SPice10209

Example: Read and display SPice10209 with full TSP and LCD handshaking


Example: Simple read and display using SPice10209

This sample program provides a very simple read-and-display function for one channel of the SPice10209. In effect it makes an MMi99/200 into a basic thermometer. By an easy change you can set the program to read in °C or °F.

This is a single task program which fully occupies the SPLat processor. Most of the time it is just waiting for the TSP interface to complete its previous task. The program listing is interspersed with detailed comments to explain what is going on. The file is also located in the examples supplied with SPLat/PC, file name TC-1.SPT.

;TC-1.SPT: A minimal SPice10209 program for MMi200 (or MMi99).
;The SPice10209 thermocouple interface is a Tiny Serial Peripheral (TSP).
;TSP uses a serial data protocol to communicate between the SPLat controller
;and an add-on (SPice) board.

;============== I/O allocations for MMi99/200 ==============

iFault10209     iEQU            17              ;This SPice pin is a direct hardware fault flag

oWaitSignal     oEQU            15              ;Front panel LED, signals waiting for TSP

oActivity       oEQU            14              ;Activity monitor

oFault10209     oEQU            10              ;Front panel LED, signals SPice10209 fault

oFaultyTSP      oEQU            9               ;Front panel LED, signals TSP fault

;============== Initialize the UV interface ==============

;Only those pins used by the SPice10209 need be configured.

;Leave others as defaults, which is the safest policy

                setu            2,2             ;set all as inputs

                setu            3,2

                setu            4,2

                SpiceConfigU                    ;Configure the SPice pins according

;                                                to nonzero values in U

;============= Initialize the TSP interface ==============

;The Tiny Serial Peripheral (TSP) interface on the controller uses 2 of the SPice pins.

;The TSPReset instruction configures those pins correctly (irrespective of any

;previous configuration) as well as sending a reset instruction to the SPice10209 board

                TSPReset                        ;Enable TSP interface driver and pins

;All further TSP communication with the SPice10209 goes via the U register.

;As with all TSP transfers for byte, word and long data types, all data is

;right justified in U0 through U3. Address 8 in the SPice10209 is the

;filter time constant register.

                SetU            3,4             ;Set up filter time constant 1=min, 4=max

                TSPPutByteL     8               ;Send filter Time Constant to the board

;==== End of initialization ==============

;===== Main loop ==========================

;The main loop of this program does just two things endlessly. It reads

;one channel of the SPice10209 thermocouple board and it displays the result

;on the onboard LCD (OBLCD).

Loop:

                GoSub           ReadTC          ;Subroutine to get one TC reading to W

                OBLCD_SetCur    0,0             ;Position LCD cursor

                OBLCD_fDispW    8,2             ;Display number in W

                OBLCD_SpclChar  223             ;Degree sign

                OBLCD_Text      "C"             ; C or F

                InputO          oActivity       ;Activity indicator

                NOT

                Output          oActivity

                GoTo            Loop

;============== End of main loop ==============

;==============  Subroutines ==============

;Read SPice10209 Ch 1, return the result converted to degrees in W. Uses Q and UV.

ReadTC:

                TSPGetWordL     0               ;Instigate a reading from SPice10209

                                                ;You can patch the address for ...

                                                ; 0 Ch 1 unfiltered

                                                ; 2 Ch 2 unfiltered

                                                ; 4 Ch 1 filtered

                                                ; 6 Ch 2 filtered

;Executing TSPGetWordL sends a data request to the SPice10209 board.

;We then have to wait for the data to become available.

;TSPBranch tests the status ...

ReadTCWait:

                ON              oWaitSignal     ;Signal waiting for TSP

                TSPBranch                       ;Test TSP status

                Target          ReadTC          ;0 Idle .. strange

                Target          ReadTCWait      ;1 Still busy

                Target          ReadTCGotData   ;2 Have data

                Target          ReadTCFault     ;3 Fault in TSP interface

;When TSPBranch returns 2 (got data) the data requested in the preceding

;TSPGet is automatically transferred to the U register bytes U0-U3.

;The TSPBranch is absolutely neccessary in order to trigger that data transfer.

;After the data is transferred the SPice10209 will drive SPice pin 6

;(input 17 on MMi99/200) ON if there is a channel fault.

ReadTCGotData:

                Off             oWaitSignal     ;diagnostic

                Input           iFault10209     ;SPice10209 fault input

                Output          oFault10209     ;display

                floatFromU      0               ;Convert reading to FP

                fLoadQ          0.25            ;Scale factor. 0.25 for deg C, 0.45 deg F

                fMul

                fLoadQ          0               ;Offset. 0 for Deg C, 32 for deg F

                fAdd

                Return

;If the TSP interface returns a fault we could have a serious problem.

;(Don't confuse with a fault from the SPice10209, which could be a simple

;open thermocouple or non-existent channel).

;In this example all we can do is display it then soldier on.

;You can induce this fault by simply removing the SPice10209 board (power down first!)

ReadTCFault:

                Off             oWaitSignal     ;diagnostic

                On              oFaultyTSP      ;Signal fault

                Pause           50              ;Show for 500mS

                Off             oFaultyTSP

                GoTo            ReadTCWait


Example: Read and display SPice10209 with full TSP and LCD handshaking

The TSP interface can take appreciable time to complete some operations. This is because it uses relatively slow serial communications between the SPLat controller and the peripheral. If you try and hit the TSP interface with an instruction before it has completed the previous one, it will stall the SPLat processor while the pending instruction completes.

This example makes full use of the status information available from the TSP, the SPice10209 and the OBLCD to create a temperature display task that uses the absolute minimum of processor time. This task is one of two tasks in a multitasking structure. The other task is a simple binary count in outputs 0-7. Just observing how fast that is counting proves that the temperature task is using very little processor time. The file is also located in the examples supplied with SPLat/PC, file name TC-2.SPT.

;TC-2: A more elaborate SPice10209 program for MMi200 (or MMi99)

;This program contains two separate tasks in a multi-tasking structure.

;You should study and understand the Multitasking Tutorial in the

;SPLat Knowledge Base before attempting to understand this program.

;The workings of the multitasking are not explained here!

;The first task reads the SPice10209 and displays temperature on

;the OBLCD. It uses full polling of the SPice10209 and of the LCD, so

;it will never stall (tie up) the processor while waiting for either

;to be ready to do something.

;The second task is a simple binary count in outputs 0-7.

;============== I/O allocations for MMi99/200 ==============

iFault10209     iEQU            17              ;This SPice pin is a direct hardware fault flag

i10209Rdy1      iEQU            13              ;Reading available flag ch 1 of Spice10209

i10209Rdy2      iEQU            14              ;Reading available flag ch 2 of Spice10209

oWaitSignal     oEQU            15              ;Front panel LED, signals waiting for TSP

oActivity       oEQU            14              ;Activity monitor

oFault10209     oEQU            10              ;Front panel LED, signals SPice10209 fault

oFaultyTSP      oEQU            9               ;Front panel LED, signals TSP fault

;=============== RAM ==============================

TempAddr        mEQU            20              ;2 bytes for a bookmark for temperature task

CountAddr       mEQU            22              ;2 bytes for a bookmark for Counter task

TempRdg         mEQU            24              ;4 bytes for temperature reading (floating point)

;============= Initialization ============================

;Both tasks require some initialization.

                GoSub           TempInit        ;Temperature display task

                GoSub           CountInit       ;Counter task

;==== End of initialization ==============

;===== Main loop ==========================

Loop:

                GoSub           DoTemperature   ;Task to update temperature display

                GoSub           Counter          ;Task to update LED Counter

                GoTo            Loop

;============== End of main loop ==============

*************** Temperature monitor/display task ********************

DoTemperature:

                Resume          TempAddr

TempInit:      ;Initialize TSP and SPice10209

                setu            2,2             ;set all as inputs

                setu            3,2

                setu            4,2

                SpiceConfigU                    ;Configure the SPice pins according

                TSPReset                        ;Enable TSP interface driver and pins

                SetU            3,4             ;Set up filter time constant 1=min, 4=max

                TSPPutByteL     8               ;Send filter Time Constant to the board

TempSet0:

;In state 0 we poll the TSP interface using TSPBranch to see if it is idle

;(capable of handling a transfer). If it is, we then test if the SP10209

;has a reading. When both conditions are satisfied we move down to the next state

;to acquire the reading.

Temp0:

                Suspend         TempAddr

                TSPBranch                       ;Test state of the TSP interface

                Target          Temp0_Idle      ;If idle go test for TC rdg

                Target          Temp0           ;Should not be busy at this stage

                Target          Temp0           ;Should not have data at this stage

                Target          TempSet2        ;Handle TSP interface fault

;The following line must change if you want to monitor the other SPice10209 channel

Temp0_Idle:

                InputF          i10209Rdy1

                GoIfF           Temp0 ;Test if a reading is available

TempSet1:

;The TSP interface is idle (available) AND the SPice10209 has a reading

;In state 1 we request a reading from the SPice10209 then sit and wait for

;an outcome.

                TSPGetWordL     0                ;Initiate readout of the data

                ON              oWaitSignal      ;Display that we are waiting

Temp1:

                Suspend         TempAddr

                TSPBranch                       ;Test state of the TSP interface

                Target          Temp0           ;Should never be idle at this stage

                Target          Temp1           ;Waiting for reading

                Target          TempSet3        ;Have a reading. Result is now in U

                Target          TempSet2        ;Handle TSP interface fault

;There is a reading available. The TSPBranch we just did transferred it to U

;In state 3 we access the reading and convert to degrees,

;then sit and wait for the LCD to be free

TempSet3:

                floatFromU       0              ;Convert to a Floating point number in W

                fLoadQ           0.25           ;Scale factor

                fMul

                fStore           TempRdg        ;Save result in degrees

                InputO           oActivity      ;Update activity indicator

                NOT

                Output           oActivity

                Input            iFault10209    ;Read the SPice10209 fault flag ..

                Output           oFault10209    ; .. display it

;Loop to wait for the LCD to become free ...

Temp3:

                Suspend          TempAddr

                OBLCD_GoIfBusy   Temp3          ;Wait for the LCD

;State 4 sets the LCD cursor then waits for the LCD to complete the operation

TempSet4:

                OBLCD_SetCur     0,0            ;Position LCD cursor

Temp4:

                Suspend          TempAddr

                OBLCD_GoIfBusy   Temp4          ;Wait for the LCD

;State 5 displays the reading then waits for the LCD to complete the operation

TempSet5:

                fRecallW         TempRdg

                OBLCD_fDispW     7,2            ;Display reading

Temp5:

                Suspend          TempAddr

                OBLCD_GoIfBusy   Temp5          ;Wait for the LCD

;State 6 displays a degree sign then waits for the LCD to complete the operation

TempSet6:

                OBLCD_SpclChar   223

Temp6:

                Suspend          TempAddr

                OBLCD_GoIfBusy   Temp6          ;Wait for the LCD

;State 7 displays a 'C' then waits for the LCD to complete the operation

TempSet7:

                OBLCD_Text       "C"

Temp7:

                Suspend          TempAddr

                OBLCD_GoIfBusy   Temp7          ;Wait for the LCD

;End of the process. Loop back and wait for the next reading.

                GoTo             TempSet0

;Handle TSP interface fault. In this case simply flash a front panel LED

TempSet2:

                TSPReset                         ;Reset TSP interface on fault

                On              oFaultyTSP       ;Visual signal

                SetTimer        4,50             ;500mS. Allow it to be on for this long

Temp2:

                Suspend         TempAddr

                Test            4                ;Timing?

                GoIfT           Temp2            ;G/ yes

                Off             oFaultyTSP       ;Timeout: Turn off indicator

                GoTo            TempSet0         ;Try the whole thing again

***************  Counter task ************************************

;A simple binary count in outputs 0-7. You can only actually see the top

;2 bits counting, indicating how fast it is. Insert a short Pause before

;the Resume to slow it down.

CountInit:

CountLoop

                LoadX            'FF

                OutputM          0

                Suspend          CountAddr

                InputOM          0

                IncX

                GoTo             CountLoop

Counter:

                Resume           CountAddr


SPice10211 - Analog I/O

(Illustrated mounted on MS12 controller)

The SPice10211 adds analog input and output capacity to a compatible SPLat controller. It was design primarily as a companion to the MS12/MS120, which lacks onboard analog, but is also useful for adding to the analog capabilities of other SPLat controllers. The board features:

  • 2 analog inputs configurable for 0-5V, 0-10V, 0-20mA or thermistor inputs
  • 2 analog outputs, 0-10V range
  • 2 potentiometers that can be read by your program as additional analog inputs. These are useful for providing field presettable parameters such as a timer interval or scale factor.

The temperature measurement uses user-supplied thermistors to perform temperature measurement at very low cost. It is suitable for applications where modest accuracy and resolution are required. Typically achievable accuracy and resolution figures are 0.2°C to 0.5°C with calibration. Without calibration accuracy is typically 2-4°C. The system is mainly suitable for temperatures from -30°C to +300°C, though thermistors are available that will work up to 700°C

The SPice10211 is a Touch of SPice board. This is the name applied to one of the several expansion systems supported within the SPLat Expansion Framework. The board may be used with MMi99/200/201/202, MS12/MS120 and SL99/SL100 controllers. It does not follow that future controllers will also support it ... the Touch of SPice system is a very low cost expansion scheme that does not guarantee compatibility across the whole family of SPLat controller products.

Previous Topic

Next Topic

In This Chapter

SPice10211: Fitting to controller

SPice10211: Power supply requirements

SPice10211: Analog outputs

SPice10211: Analog inputs

SPice10211: Potentiometers

SPice10211: Programming


SPice10211: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. If you are using an MMi controller, remove the two option links
  2. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  3. Identify the matching single in-line pin connector on the controller board.
  4. Experimentally plug the two boards together, taking care to align the connectors
  5. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  6. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  7. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice10211: Power supply requirements

The board draws its power from the host controller board. If you are using either analog input in thermistor mode, the supply voltage needs to be at least 2V more than the programmed thermistor drive voltage. Otherwise the supply voltage can be anything over 12V.


SPice10211: Analog outputs

The above picture shows the SPice10211 analog output connections. Each output can generate a 0-10V signal, with a maximum load current of 5mA, corresponding to a 2KOhm load. The outputs have internal current limiting (which is not the same as being short circuit proof!).

Note: Which actual analog inputs and outputs the board maps to depends on which model of SPLat controller it is attached to. The mapping is detailed under Programming.

NOTE: Analog output 2 is shared with the thermistor drive circuit. If you are using a thermistor you cannot independently use analog output 2. In the case of the SL99 and MMi99/200 this also applies if you are using thermistors on the main board.

The analog output voltage is a positive voltage measured with respect to the 0V terminal. It is programmed using the analog output instructions. With X=0 this will result in 0V out, with X=255 it will result in 10V out.

The output resolution is 10 bits (1024 steps between 0 and full scale). The output is generated as a filtered pulse width modulated (PWM) signal. The filter consists of a simple 100mS RC low pass filter. For a full scale step output change, say from 0V to 10V, this will take 230mS to settle to within 10% of the full change, 450mS to settle to 1% and 550mS to settle to 0.4% (= 1 bit, i.e. the resolution of the system).

How to blow up the analog outputs

Here are some ways of blowing up the analog outputs:

  1. Connect the output to any positive voltage over 10V and program a 0V output.
  2. Short the output to 0V for extended periods of time (it may survive)

Connect the output to a negative voltage, even momentarily.


SPice10211: Analog inputs

The picture above shows the connector used for analog inputs. An input is configured by using one of the supplied jumpers to bridge certain pins.

Note: Which actual analog inputs and outputs the board maps to depends on which model of SPLat controller it is attached to. The mapping is detailed under Programming.

There are two analog input channels on the SPice10211 board. Each input can be configured to operate on 0-10V, 0-20mA or as a temperature measuring input (using an external thermistor sensor). In all cases the input uses one input pin and one common pin. The common pins on the analog input connector are tied to the board's 0V terminal. Please see the article on common terminology for an explanation of the 0V terminal.

Each channel has a 4-pin configuration jumper field. A single jumper (shunt) on a pair of jumper pins selects the operating mode (V=voltage, I=current or T=temperature). In the above picture channel 2 is set for temperature and channel 1 is set for current.

Using voltage mode

To use an analog input in voltage mode you set its jumper to bridge the two pins either side of the letter V and apply the input voltage between the input (positive) and the common (most negative). When an AnIn* instruction is executed, you will get a number between 0 and 255 in the X-register. 0 represents 0V and 255 represents 10V.

From dialect 18 you can also use the fAnIn instruction. This will return a normalized floating point number in W which ranges from 0 at 0V in to 1.000 at 10V in. The range 0 to 1.0 is divided into 1023 steps of 0.00097752 (on older controllers, viz MMi200, MMi201, MS12, the range is divided into 255 equal steps of 0.0016334). fAnIn selects the required channel with a numeric argument. Channel number are identified under programming. The channel number is jndexed.

In voltage mode the input resistance is 200KOhm.There is an input noise filter comprising a single RC time constant of 5mS.

If you leave the jumper out, you will get a 0-5V range instead of 0-10V.

Using current mode

To use an analog input in current mode you set its jumper to bridge the two pins either side of the letter I. You then feed the current to be measured into the input pin, with the return out of the common pin.

When an AnIn instruction is executed, you will get a number between 0 and 255 in the X-register. 0 represents 0mA and 255 represents 20mA. At 4mA you will get a reading of 51 (plus or minus any conversion error). You can also use fAnIN, which will return 0 for 0mA, 1.0 for 20mA and 0.2 for 4mA (plus or minus any conversion error). fAnIn selects the required channel with a numeric argument. Channel number are identified under programming.The channel number is jndexed.

In current mode the input resistance is 250Ohms. There is an input noise filter comprising a single RC time constant of 10mS.

Accuracy and resolution (voltage and current modes)

The accuracy is about 1.5% of full scale. Resolution is 10-bit (0.1% of full scale) on late model controllers. On older controllers, viz MMi200, MMi201, MS12, the resolution is 8-bit (0.4% of full scale).

Using thermistor mode

To use the analog input in temperature mode you set its jumper to bridge the two pins either side of the letter T and connect a suitable thermistor between the analog input pin and common. There are two basic types of thermistor: Positive Temperature Coefficient (PTC) and Negative Temperature Coefficient (NTC). The latter, NTC, is the type used for temperature measurement with SPLat. Typically, an NTC thermistor is specified as having a certain resistance at 25 degrees C.

The adjacent diagram shows the thermistor measuring circuit.

MMii99 thermistor circuit

The thermistor is the bottom resistor in a voltage divider. The top resistor is a fixed onboard 10.25KOhm resistor. The voltage divider is driven by a programmable voltage that appears as analog output 8. This is an "onboard output" that is used for thermistor drive and is also available on the SPice connector, but does not "use up" either of the two externally available analog outputs. The thermistor drive voltage can be set to anything between 0V and 20V. Note that the board supply voltage must be at least 2V greater than the intended drive voltage. The drive voltage is programmed using an fAnOut instruction to the appropriate channel defined under programming. With W=0 this will result in 0V out, with W=1 it will result in 20V out.

In this thermistor mode the full scale analog input voltage is 5V (whereas the thermistor output drive has a full scale of 20V).

A thermistor is an electronic component that changes resistance with temperature. Don't confuse this with an RTD sensor, which also changes resistance with temperature but is much less sensitive! The warmer the thermistor gets, the lower its resistance. Hence, a reading of the input voltage can be used to calculate the thermistor temperature.

Accuracy and resolution depend on the thermistor you use and how you program it. The best achievable figures are about 0.2°C for both, if the system includes provision for software calibration. The SPLat Knowledge Base contains a tutorial on thermistor temperature measurement using SPLat boards.

How to blow up the analog input

Here are some ways of blowing up the analog inputs:

  1. In any mode, connect the input to a voltage greater than the positive supply or negative with respect to the 0V terminal.
  2. In current mode, apply more than 7.5V to the input. This will overheat the current shunt resistor and lead to loss of accuracy or, in the extreme, damage the board.

Analog inputs and SPLatLink

The board is capable of returning analog readings via SPLatLink without any specific provisions on your part.


SPice10211: Potentiometers

The SPice10211 contains two potentiometers, labelled Trim1 and Trim2. The settings of these can be read by your program using analog input instructions. That gives you the facility to easily provide field adjustable parameters. Possible uses include

  • "Analog" timer adjustment
  • Field calibration settings
  • Motor speed and acceleration presets
  • Setpoint control

The key to all this is that it is your program that creates the relationship between the potentiometer setting and whatever it is controlling.

The potentiometers are read using analog input instructions. They will result in a number between 0 and 255 in X.

All analog inputs are subject to errors. This can include missing codes, i.e. certain input values that for one reason or another never get generated. Your program should be designed so it cannot "hang up" or otherwise fail due to missing codes. In particular with something like the potentiometers, don't rely on being able to get the extreme values like 0 and 255, or even 1 and 254.

Note: Which actual analog inputs the potentiometers map to depends on which model of SPLat controller the SPice10211 is attached to. The mapping is detailed under Programming.


SPice10211: Programming

Once installed on a SPLat controller, the SPice10211 appears as several regular analog and digital I/O points. The following table shows the mappings for all the I/O on the board. Numbers in parenthesis are the numeric analog channel numbers used with fAnIn and fAnOut.

 | Cnctr pin | SLXXX | MMiXXX | MS12X
Analog input 1 | 4 | AnInC (2) | AnInE (4) | AnInC (2)
Analog input 2 | 5 | AnInD (3) | AnInF (5) | AnInD (3)
Analog output 1 | 8 | AnOutH (7) | AnOutJ (9) | AnOutJ (9)
Analog output 2 /; Thermistor drive voltage | 9 | AnOutG (6) | AnOutI (8) | AnOutI (8)
Trim 1 | 7 | AnInF (5) | AnInH (7) | AnInF (5)
Trim 2 | 6 | AnInE (4) | AnInG (6) | AnInE (4)

initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10211 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice12011.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10211.

         ClrU
         setu         0,3         ;set spice connector pin 4 as analogue input
         setu         1,3         ;set spice connector pin 5 as analogue input
         setu         2,3         ;set spice connector pin 6 as analogue input
         setu         3,3         ;set spice connector pin 7 as analogue input
         setu         4,4         ;set spice connector pin 8 as analog output
         setu         5,4         ;set spice connector pin 9 as analog output
         SpiceConfigU

If you are using a thermistor circuit you will also need to initialize the thermistor drive voltage to the required value with code like this:

         LoadX        XXX

         AnOutZZZ

In this code replace XXX with the Vfeed value required. You will generate this when you work through the thermistor temperature measurement tutorial. On the SPice10211 the Vfeed range is 0 to 20V (representing XXX = 0 to 255).

Note: The maximum Vfeed value you can use is limited by the supply voltage to the board. The Vfeed generator can at most get to about 2V less than the supply voltage. Hence, with say a 12V supply you can at best design for a 10V Vfeed (XXX=127). If you are using an unregulated supply you must take into account voltage variations and ripple. With a 24V regulated supply you will be able to use the full Vfeed range up to 20V.

Replace ZZZ with the analog output pin used for Vfeed (see table above).

Programming the thermistor inputs

If you are using the thermistor temperature measurement feature you must initialize the drive voltage as described above. Thereafter you get a raw reading from the thermistor into X using the appropriate Anin instruction in the table above. The thermistor temperature measurement tutorial shows you how to convert the raw readings into temperatures.

After initialization there will be a delay before the Vfeed voltage settles down. Your program should not perform any control action based on a temperature reading for at least 500mS after initialization.

Note that during debug with SPLat the SPice board may not show up correctly in the I/O window unless the board has run the initialize code. You can fix this by translating and running the above initialization sequence in the board. Add a line

a     goto    a

at the end to prevent translation and runtime errors.

Programming the potentiometers

The potentiometers simply appear as two analog inputs.

  Previous Topic

  Next Topic

In This Section
Example: "Analog" timer setting using potentiometers
Example: Reading out the SPice10211 potentiometers


Example: "Analog" timer setting using potentiometers

The SPice10211 contains two onboard potentiometers that can be used for various parameter setting applications. In this example potentiometer "Trim1" is used to control the duty cycle of an output pulse train on output 0. The pulse train generates one pulse every 2.55 seconds, with the width of the pulse being proportional to the setting on the potentiometer.

;TimePot.spt  Demo program for SPice10211 potentiometer

;Potentiometer Trim1 is read in and used to control the duration of an

;output pulse. A fresh pulse is started every 2.55 seconds using time 5,

;and its duration is varied between 0 and 2.55 seconds using timer 4

;The program is written for an MS12. The output pulse train is produced on output

;8 because that is a visible LED.

                On              20              ;LCD backlight (just in case)

;Initialize the SPice connector for the SPice10211

                ClrU

                setu            0,3             ;set spice connector pin 4 as analog input

                setu            1,3             ;set spice connector pin 5 as analog input

                setu            2,3             ;set spice connector pin 6 as analog input

                setu            3,3             ;set spice connector pin 7 as analog input

                setu            4,4             ;set spice connector pin 8 as analog output

                setu            5,4             ;set spice connector pin 9 as analog output

                SpiceConfigU

Loop:

                Test            5               ;Check if time for another pulse

                GoIfT           Loop

                SetTimer        5,255           ;Restart the pulse interval timer

                AnInF                           ;Read the potentiometer

                float                           ;Convert to floating point

                fSetTimer       4               ;Set pulse duration timer timer

                ON              8

Timing:

                Test            4               ;Check if pulse duration has expired

                GoIfT           Timing

                Off             8

                GoTo            Loop


Example: Reading out the SPice10211 potentiometers

The following sample program will read in the two potentiometers on the SPice10211 and display t he raw readings on the LCD on an MS12

;PotDisp.spt   Display SPice10211 potentiometers on MS12 LCD

                ON              20              ;LCD backlight

;Initialize the SPice connector for the SPice10211

                ClrU

                setu            0,3             ;set spice connector pin 4 as analog input

                setu            1,3             ;set spice connector pin 5 as analog input

                setu            2,3             ;set spice connector pin 6 as analog input

                setu            3,3             ;set spice connector pin 7 as analog input

                setu            4,4             ;set spice connector pin 8 as analog output

                setu            5,4             ;set spice connector pin 9 as analog output

                SpiceConfigU

Loop:

                AnInF

                OBLCD_SetCur    0,0

                OBLCD_UDecDispXFW

                AnInE

                OBLCD_SetCur    0,7

                OBLCD_UDecDispXFW

                GoTo            Loop


SPice10212 - 3-phase VAC measurement

The SPice10212 provides 3 channels of AC voltage measurement. It is primarily intended for monitoring up to 3 phases of the mains (line). Input voltage isolation and scaling is provided by external, user supplied transformers.

Typical applications include standby generator controls and phase failure monitoring in large HVAC or industrial compressor systems.

Safety warning: This board is intended for use with user supplied transformers to isolate the board from the voltages being measured. It is your responsibility to make sure you use appropriately safety rated transformers and wire them safely and correctly.

The board comprises three independent full wave rectifiers, smoothing filters and calibration potentiometers. It is intended that the transformers you supply will scale the input voltage to nominally 24VAC full scale. In other words, you would use transformers with a primary voltage equal to what you want to measure and 24VAC secondary windings which you connect into the SPice10212 board. The calibration potentiometers allow you to adjust out the effects of transformer regulation.

Note: This is not a precision measurement system. Accuracy depends on a number of factors, mainly how well you calibrate the system. Because it uses simple rectification, it will be inaccurate at low input voltages. Resolution is 1/1024 of the full scale range you aim for. This will be roughly 1V for 240VAC full scale and roughly 0.5V for 120VAC full scale. This is an average responding system, not a true RMS system. The frequency range is 50/60Hz. See also Nonlinearity correction.

Previous Topic

Next Topic

In This Chapter

SPice10212: Fitting to controller

SPice10212: Power supply requirements

SPice10212: Measurement inputs

SPice10212: Calibration

SPice10212: Nonlinearity correction

SPice10212: Programming


SPice10212: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  2. Identify the matching single in-line pin connector on the controller board.
  3. Experimentally plug the two boards together, taking care to align the connectors
  4. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  5. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  6. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice10212: Power supply requirements

The board draws its power from the host controller board.


SPice10212: Measurement inputs

The SPice10212 has 3 pairs of input pins. They are located on a single 6-way connector. The pairs are labelled A, B and C. Each pair is for a separate measurement circuit.

The inputs are designed to be connected to external 24VAC isolated transformer windings. For 3-phase measurement you will need 3 transformers.

You could also use an auxiliary winding on a transformer that is powering other parts of your system, but this could result in loss of accuracy due to interaction between the transformer's regulation and the other loads.

Do not use a transformer winding that is also being rectified to provide a DC power supply to the controller or anything connected to the controller. At best this will make for inaccurate readings. At worst it will make smoke.


SPice10212: Calibration

You need to calibrate the board yourself. You may find it easiest to calibrate with a special purpose program loaded into the SPLat controller, at least initially. When you gain experience with the process you could build a calibration mode into your final SPLat program.

Caution: Chances are you will be working with lethal voltages. Only do this work if you fully understand how to work safely with dangerous voltages. The use of the term "isolation transformer" does not imply they make it safe.

The process is easiest if you have a SPLat board with an onboard LCD display. In that case the readings can be displayed on the LCD. Otherwise a calibration program can store readings in RAM for readout using SPLatLink.

Assuming you are calibrating your system to measure 3 phases of a 240V circuit, you will need the following for calibration:

  • Your SPLat board set up with the SPice10212 and wired via the isolation transformers to the 240V source.
  • A multimeter capable of accurately measuring the 240VAC and a safe way of connecting it to the 240V.
  • A DC power supply for the SPLat
  • Computer and programming cable
  • A calibration program, translated and downloaded into the SPLat board. Some example programs are provided later under Programming.

Because transformers can have some unit to unit variation, it is best to use the actual transformers, not proxies. You absolutely must not use a different model of transformer for calibration to the one you are using "live".

Before you start the actual calibration you need to decide what full scale range you are aiming for. The SPice10212 fitted to a MMi99/200, an SL99 or an MS12 will produce an analog reading of 255 counts at full scale. If for example you selected 255VAC full scale that would give you exactly one count per volt. If now your nominal voltage being measured is 240VAC, a 255VAC full scale would allow 6.25% over-range (255/240=1.0625). In practice it would probably be better, when monitoring mains (line) voltages, to go for 10% or even 15% over-range, so with a 240V nominal system you would plan for 264V or 276V full scale.

Once you have decided on a full scale voltage determine the scale factor between voltage and analog input readings (count). If 255 counts represents say 264VAC, then for any given voltage

Counts = Voltage * 255/264

So the scale factor for converting voltage to counts is 255/264=0.966. Call this factor "K"

You must calibrate each of the 3 channels separately. Repeat this procedure for each channel.

  1. Apply 240V to the transformer primary (the actual voltage will be whatever is present on the line).
  2. Check the secondary voltage with a multimeter, measuring at the input pins of the SPice10212. It should be in the range 24 to 33V (assuming the primary is at its nominal voltage). The high upper limit is brought about because a transformer will always produce more than its nominal voltage when unloaded. It is designed to drop to the nominal voltage when fully loaded. In general, the smaller the transformer the larger the voltage difference will be. The SPice10212 is designed to accommodate transformer voltage ranges between 24V and 33V.
  3. Observe the raw analog to digital reading produced by the calibration program for the channel you are working on. If you are using a controller with an LCD you should be using a program that displays it on the LCD. Otherwise you should observe it via the SPLatLink feature of SPLat/PC. This is documented in the SPLat/PC help file (not in the SPLat Knowledge Base). Just search for SPLatLink. Make sure you are working with a recent version of SPLat/PC.
  4. Measure the input voltage to the transformer, e.g. the 240VAC line. Chances are it will not be exactly at the nominal value, because the mains fluctuates. Say for example you read 236V.
  5. Calculate what the raw analog input count should be, given the current mains (line) voltage. You get this by multiplying the voltage by the scale factor K you calculated earlier. With the example numbers, where K=0.966 and the the voltage is 236V, the raw reading should be 236*0.966=228 counts.
  6. Adjust the calibration potentiometer associated with the channel you are working on until the reading matches the required value. While doing this check that the input voltage is remaining steady.

It can be very helpful if you have a Variac to vary the input voltage. This helps you select your calibration point and helps you gain confidence in the system.

Make sure you don't calibrate at the full scale value, i.e. with 255 counts. If you are adjusting for 255 counts there's no way of knowing if the input is over 255 (e.g. should read 256).


SPice10212: Nonlinearity correction

The SPice10212 uses a simple bridge rectifier circuit followed by an averaging filter. It is not a precision rectifier. This means that the response of the board will be somewhat nonlinear, because of the forward voltage drop of the rectifiers. In addition you have the nonlinearity of the transformers to contend with. This is even less predictable than the nonlinearity due to the diodes.

Because the intended use of the board is for monitoring variations in mains (line) voltages around a nominal value of say 240VAC, the nonlinearity will usually have little practical effect. Providing you have calibrated at or near the nominal operating voltage, any nonlinearity errors within say 10% of nominal would amount to no more than a count or two.

There may be situation where you would like to do better. For a given transformer and full scale combination it is feasible to establish a polynomial equation that converts raw readings into voltage with good linearity. It should be possible to get good results with this method, providing you always use the same type of transformer. Note though that in our experience over thousands of small transformers an occasional "rouge" one will have a different set of characteristics. Hence, your testing procedure should include a spot check on linearity.

We use an Excel spreadsheet to determine the polynomial coefficients. The basic process is as follows:

  1. Calibrate to a full scale count near 250.
  2. Using a Variac, take a series of readings of counts versus input voltage. We used 5, 10, 25, 50, 100,150, 200 and 250V.
  3. In Excel, plot voltage (Y axis) versus count (X axis)
  4. Right click the graph and select "Add trendline..."
  5. Set type = polynomial, and order = 3, and in the Options tab select "Display equation on chart"
  6. You should now see an equation on the graph. Right click the equation and select "Format data labels..."
  7. Select the Number tab and set the format to Scientific with 6 decimal places.

You should now have the equation displayed with heaps of precision. Extract the coefficients and insert them in our sample program.


SPice10212: Programming

Once installed on a SPLat controller board the SPice10212 appears as 3 analog inputs. The following table shows the mapping for various controllers. Numbers in parenthesis are the numeric analog channel numbers used with fAnIn.

 | Cnctr pin | SLXXX | MMiXXX | MS12
Phase A | 4 | AnInC (2) | AnInE (4) | AnInC (2)
Phase B | 5 | AnInD (3) | AnInF (5) | AnInD (3)
Phase C | 6 | AnInE (4) | AnInG (6) | AnInE (4)

initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10212 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice10212.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10212.

         ClrU
         setu         0,3         ;set spice connector pin 4 as analog input
         setu         1,3         ;set spice connector pin 5 as analog input
         setu         2,3         ;set spice connector pin 6 as analog input
         SpiceConfigU

This section contains a few programming examples. You can use them as a starting point for your own programs.

In This Section

Example: SPice10212 calibration with LCD

Example: SPice10212 calibration without LCD

Example: SPice10212 nonlinearity correction


Example: SPice10212 calibration with LCD

This is a simple calibration program for controllers with built in LCD. It has been coded for the MMi99/200, but changes for the MS12 are noted.

;cal200.spt SPice20212 demo/calibration using MMi200 with LCD
;Changes for MS12 are noted.

;Use for MS12 only              ON              20      ;MS12 backlight

;Initialize the SPice connector for 3 analog inputs

                SetU            0,3

                SetU            1,3

                SetU            2,3

                SPiceConfigU

;Main program loop:

Loop:

;This section displays the raw analog readings on the

;top line of the display. Full scale is always 255 counts

                AnInE                   ;Use AnInC on MS12

                OBLCD_SetCur    0,0

                OBLCD_UDecDispXFW

                AnInF                   ;Use AnInD on MS12

                OBLCD_SetCur    0,6

                OBLCD_UDecDispXFW

                AnInG                   ;Use AnInE on MS12

                OBLCD_SetCur    0,12

                OBLCD_UDecDispXFW

;This section displays the readings as voltages.

;The scale factor initially loaded into Q will determine the

;full scale range, i.e. the voltage corresponding to 255

;raw counts.

;The scale factor is Vfs/255, where Vfs is the required full scale voltage.

                fLoadQ          0.5176  ;1.035  for FS=265V (240V+10%)

                                        ;0.5176 for FS=132V (120V+10%)

                AnInE                   ;Use AnInC on MS12

                OBLCD_SetCur    1,0

                Float

                fMul

                OBLCD_fDispW    3,0

                AnInF                   ;Use AnInD on MS12

                OBLCD_SetCur    1,6

                Float

                fMul

                OBLCD_fDispW    3,0

                AnInG                   ;Use AnInE on MS12

                OBLCD_SetCur    1,12

                Float

                fMul

                OBLCD_fDispW    3,0

                GoTo            Loop


Example: SPice10212 calibration without LCD

This program allows you to calibrate on a controller board that has no LCD. Instead, readout is performed using the SPLatLink facility in SPLat/PC. You can learn how to use SPLatLink from the SPLat/PC help file. Just search for SPLatLink.

;Cal99.spt SPice20212 demo/calibration using SL99
;The challenge here is that the SL99 has no numeric display.
;What we do is to store the readings in RAM then we can read
;them out using SPLat link (the SL button on the Module window)
;Once the program has been run (and configured the SPice connector)
;it's also possible to monitor the analog inputs in the
;analog window of SPLat/PC.

;Initialize the SPice connector for 3 analog inputs

                SetU            0,3

                SetU            1,3

                SetU            2,3

                SPiceConfigU

;Main program loop:

Loop:

                AnInC                   ;Phase A

                Store           0

                AnInD                   ;Phase B

                Store           1

                AnInE                   ;Phase C

                Store           2

                GoTo            Loop


Example: SPice10212 nonlinearity correction

This program demonstrates the polynomial method of linearizing the raw readings. Note that the actual coefficients used are specific to our test transformer. You must determine your own coefficients and use them in your program. Only one channel has been linearized. The program uses the MMi99/200 LCD for display.

;Correct.spt SPice20212 demo/calibration with nonlinearity correction
;using MMi200 with LCD
;The nonlinearity correction is applied only
;to the first channel, to allow comparisons to be made.

;RAM storage

Temp            EQU             20

;Use for MS12 only              ON              20      ;MS12 backlight

;Initialize the SPice connector for 3 analog inputs

                SetU            0,3

                SetU            1,3

                SetU            2,3

                SPiceConfigU

;Main program loop:

Loop:

;This section displays the raw analog readings on the

;top line of the display. Full scale is always 255 counts

                AnInE                   ;Use AnInC on MS12

                OBLCD_SetCur    0,0

                OBLCD_UDecDispXFW

                AnInF                   ;Use AnInD on MS12

                OBLCD_SetCur    0,6

                OBLCD_UDecDispXFW

                AnInG                   ;Use AnInE on MS12

                OBLCD_SetCur    0,12

                OBLCD_UDecDispXFW

;This section displays the readings as voltages.

;For phase A the conversion is done using a correction algorithm

;based on a polynomial function.

;For the other two phases the conversion is a simple scaling.

;The scale factor initially loaded into Q will determine the

;full scale range, i.e. the voltage corresponding to 255

;raw counts.

                AnInE                   ;Use AnInC on MS12

                GoSub           Correct

                OBLCD_SetCur    1,0

                OBLCD_UDecDispXFW

;The scale factor for the other two readings

;is Vfs/255, where Vfs is the required full scale voltage.

                fLoadQ          1.0     ;1.0  for FS=255V (240V+10%)

                                        ;0.5176 for FS=132V (120V+10%)

                AnInF                   ;Use AnInD on MS12

                OBLCD_SetCur    1,6

                Float

                fMul

                OBLCD_fDispW    3,0

                AnInG                   ;Use AnInE on MS12

                OBLCD_SetCur    1,12

                Float

                fMul

                OBLCD_fDispW    3,0

                GoTo            Loop

;Correct for nonlinearity

;Implements the function

;y = 2.859320E-06 x^3 -1.011672E-03 x^2 +1.058639E+00x +3.916506E+00

;These particular coefficients were determined with a specific transformer,

;with the system calibrated for 255V full scale. A different transformer

;and/or full scale range will require different coefficients.

Correct:

                Float

                fStore          Temp

                fLoadQ          2.859320E-06            ; X^3 coefficient

                fMul

                fLoadQ          -1.011672E-03           ; X^2 coefficient

                fAdd

                fRecallQ        Temp

                fMul

                fLoadQ          +1.058639E+00           ; X^1 coefficient

                fAdd

                fRecallQ        Temp

                fMul

                fLoadQ          +3.916506E+00           ; constant offset

                fAdd

                fLoadQ          0.5                     ;Rounding

                fAdd

                Fix

                Return


SPice10213: DC motor control

The SPice10213 provides the ability for a SPLat controller to drive and speed control one or two small 24VDC permanent magnet motors. It has two outputs. Both outputs have programmable PWM (Pulse Width Modulated) speed control. One output has a reversing relay, allowing bidirectional drive control.

The reversing relay, in conjunction with a dynamic braking resistor, allows dynamic braking on the reversible output. That means the motor can be stopped very quickly.

The PWM control signals are supplied from the host SPLat controller board via SPice pins that are set up as PWM outputs. This is the mode normally associated with analog outputs. The non-reversible output can alternatively be driven off a digital (on/off) output pin. The PWM frequency is 7.8kHz (this can be altered in some controllers with CPU device setting). The output duty cycle and can be set with 10-bit precision (1024 steps) (8-bit precision (256 steps) in older controllers).

  • Two channels of PWM speed control
  • One channel is reversible with dynamic braking
  • 3A/24V motor capacity
  • Compatible with MMi99/20x, SL99/1xx and MS12x controllers

Note: The product warranty on this board does not cover blown up output transistors.

Previous Topic

Next Topic

In This Chapter

SPice10213: Ratings

SPice10213: Fitting to controller

SPice10213: Power supply requirements

SPice10213: Connecting motors

SPice10213: Programming


SPice10213: Ratings

Each output is nominally rated at 3A DC continuous, with a start-up (intermittent) rating of 15A. The power supply can be 12V to 24VDC.

The greatest strain is placed on the board, and on the power supply, if the motor is mechanically prevented from rotating ("locked rotor") and has the full power supply voltage applied to it. A similar strain occurs for a short while if the motor is turned on at full voltage, before it has started rotating. As the motor spins up to speed the "dynamo" voltage generated by the motor cancels out much of the supply voltage, and the current drops off. The start-up strain on the SPice10213 will be less if the motor voltage (PWM) is ramped up rather than turned straight on at full voltage.

You can estimate the locked rotor/startup current by measuring the motor resistance. If you use a normal multimeter, measure the resistance of the test leads and deduct from the reading you get off the motor using the same leads. Typical motor resistances are in the low ohms or less than an ohm range. The larger the motor the smaller the resistance. Divide the resistance in ohms into the supply voltage to get the locked rotor current in amps. You may also be able to get the locked rotor current off the motor name plate or data sheet.

If the locked rotor current is less than 15A the SPice10213 should unconditionally be able to drive it. If it is over 15A, it may still be OK but you must be aware of the risks. If you can guarantee the motor can never suffer a locked rotor, or if you are prepared to blow up the SPice10213 if it does lock, then providing the continuous motor running current is less than 3A it may still be OK. You should however make sure the motor gets ramped up to speed rather than getting turned on suddenly at full power. The main hazard if the board is overloaded is that the power FETs (transistors) will over-heat.

Note: The product warranty on this board does not cover blown up output transistors.


SPice10213: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. Remove power from the controller.
  2. If you are using an MMi controller, remove the two option links
  3. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  4. Identify the matching single in-line pin connector on the controller board.
  5. Experimentally plug the two boards together, taking care to align the connectors
  6. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  7. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  8. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice10213: Power supply requirements

The SPice10213 board is powered off the same power supply that provides power to the motor(s). This may or may not be the power supply that powers the SPLat controller. If you use a separate power supply it is advisable to connect the power supply common (0V) terminals together.

The power supply voltage can be 12VDC to 24VDC.

The power supply current rating needs to be enough to supply the largest amount of current the motors are likely to draw. This may require some practical tests, especially if you are using a regulated (switchmode or linear) supply. The motors will normally draw much more current at startup than running, and the response of the power supply to the high startup currents will affect system performance. For example, a power supply with foldback current limiting could fail to ever get the motor started, even if at face value it is amply rated for the running current. This situation can be alleviated by ramping up the motor PWM, but we cannot give you any hard and fast answers to cover every situation.

Bare in mind that if the power supply does go into current limit its voltage will drop. If the same power supply is also powering the controller and the voltage drops below about 8V the controller could reset and re-start the program. This could produce hazards.


SPice10213: Connecting motors

The diagram above shows two motors, M1 and M2, connected to the SPice10213.

M1 is connected to the reversible, PWM controlled output pins labelled M1A and M1B.

M2 is connected to the non-reversible PWM output between the pin labelled M2 and one of the + pins. The two + pins are connected together on the board.

The SPice10213 board must be wired directly to a DC power supply with a voltage and current rating suitable for powering the motors. Most likely you will power your SPLat controller off the same power supply, but it must be fed via its own wires directly off the power supply. Do not run wires from the power supply to the SPice10213 and from there to the controller, or even worse from the power supply to the controller and from there to the SPice10213. This would only invite all kinds of hard to trace noise problems.


SPice10213: Programming

Once installed on a SPLat controller, the SPice10213 appears as several regular analog and digital I/O points. The following table shows the mappings for all the I/O on the board. Numbers in parenthesis are the numeric analog channel numbers used with fAnIn and fAnOut.

Function | Cnctr pin | SLXXX | MMiXXX | MS12
Motor 1 direction | 4 | Output 8 | Output 16 | Output 12
Motor 1 speed | 8 | AnOutH (7); Output 12 | AnOutJ (9); Output 20 | AnOutJ (9); Output 16
Motor 2 speed; (jumper = PWM) | 9 | AnOutG (6); Output 13 | AnOutI (8); Output 21 | AnOutI (8); Output 17
Motor 2 On/Off; (jumper = DO) | 7 | Output 11 | Output 19 | Output 15

The above chart shows the SPice10213 function mappings for various controllers.

You will notice that SPice connector pins ("Cnctr pin") 8 and 9 have both AnOut (analog output) and "ordinary" (meaning digital) output assignment associated with them. This is because the SPice pins are configurable as to their function, and with the SPice10213 it makes sense to use PWM (variable speed) or simple digital on/off modes, depending on the application. You need to decide which mode you are going to use and then configure the SPice connector accordingly (more on this below).

Motor 1 is the reversible output. It uses one output for speed control (be it on/off or variable) and one digital output for direction.

Motor 2 is non-reversible. It requires only one output. However, you have a choice of two SPice pins, which are selected by an onboard jumper (highlighted above). The jumper has positions labelled "DO" and "PWM", which select SPice pins 7 and 9 respectively. The reason for this is that on the MMi99/200 and the SL99 pin 9 is used for the thermistor drive voltage if you are using that feature. In that case you would set the jumper to "DO" and sacrifice speed control on motor 2 in return for having the thermistor drive. In the "PWM" position you are using connector pin 9, in which case you are free to configure the pin for PWM or for on/off (digital) operation (that can be a bit confusing, that the PWM position can actually be configured for either mode).

Initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10213 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice12013.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10213.

         ClrU
         setu         0,1         ;set spice connector pin 4 as DO (Direction 1)

;Select one of the 2 following depending on your needs:

         setu         4,4         ;set spice connector pin 8 as PWM output

         setu         4,1         ;set spice connector pin 8 as digital output

;Select one of the 3 following depending on your needs:

         setu         3,1         ;set spice connector pin 7 as digital output (jmpr="DO")

         setu         5,4         ;set spice connector pin 9 as PWM output (jmpr="PWM")

         setu         5,1         ;set spice connector pin 9 as digital output (jmpr="PWM")

         SpiceConfigU             ;Commit the configuration

  Previous Topic

  Next Topic

In This Section
Example: Dynamic braking with SPice10213
Example: Simple motor speed control with SPice10213
Example: Advanced motor control with SPice10213


Example: Dynamic braking with SPice10213

This is a very simple example program illustrating how to apply dynamic braking.

Note: Dynamic braking with the SPice10213 is intended for and tested with small permanent magnet brushed DC motors.

The principle of dynamic braking of a DC motor is as follows: When the motor rotates, the movement of the wound coil in a magnetic field generates a voltage. During normal running that voltage will nearly equal the voltage supplied by the power supply. If the power supply is removed, the motor will continue to spin and still generate a voltage which can be measured across its terminals. The motor will then gradually slow down as its rotational energy is dissipated in friction and in the mechanical load. If, during the slow-down phase you short the motor terminals, a large current will flow (limited only by the resistance of the motor windings). The motor has now become a dynamo that is converting the mechanical energy of rotation into electrical energy, and that energy is dissipated rapidly in the motor windings as heat. The result is that the motor comes to a stop much sooner.

In practice The SPice10213 does not directly short out the motor terminals for braking. Instead it uses a series connected 1.2ohm resistor and diode in conjunction with the direction relay. The resistor limits the braking current to a safe level. The diode is present in the circuit to protect the PWM driver transistor from inductive kick-backs, and serves incidentally in the braking function.

Given the motor is running, the dynamic braking process consists of the following steps:

  1. Turn off the power to the motor. The dynamo voltage from the motor will at this point be reverse biasing the diode.
  2. Change the reversing relay to the opposite direction. This will forward bias the diode, it turns on and current flows through the diode and resistor so the mechanical energy of the motor can quickly dissipate as electrical energy in the resistor, diode and motor winding.
  3. Allow enough time for the motor to come to a halt. The time required will depend on the motor and possible inertia of its load (i.e. a big fly-wheel will take longer to stop because it contains more rotational energy), and needs to be determined for each application. Typical stop times would be 100mS to 500mS.
  4. (Optionally) restore the direction relay to the original direction. This will depend on the overall logic of your program - you just want to be sure it's set for the correct direction next time you start the motor.

Note that it's important to turn off the power (set PWM=0) before reversing the relay.

;MDrv0  SPice10213 dynamic braking example

;This program simply illustrates dynamic braking of a small DC motor using the SPice10213

;on an MS12. It has a stop/start button. The motor is driven in simple on/off mode

;(no speed control) off the motor 1 output.

iStopStart      iEQU            16              ;The Stop/Start button

oDirection      oEQU            12              ;The direction relay

oMotor          oEQU            16              ;The on/off output

;------

;Start of program

                setu            0,1             ;set spice connector pin 4 as DO

                setu            4,1             ;set spice connector pin 8 as DO

                SpiceConfigU

Idle:                   ;Back here when motor is stopped

                WaitOnK         iStopStart      ;Wait for start command

                On              oMotor          ;Run the motor

Running:

                WaitOnK         iStopStart      ;Wait for stop command

Stop:

                Off             oMotor          ;Turn off the motor

;Comment out the following line to see the effect of not using dynamic braking

                ON              oDirection      ;Apply the brake

                Pause           50              ;Allow motor time to stop

                Off             oDirection      ;Turn off the brake

                GoTo            Idle


Example: Simple motor speed control with SPice10213

The following sample program illustrates the basic speed control function and dynamic braking with the SPice10213. It drives the motor 1 (reversible) output.

;MDrv1  SPice10213 simple speed control example

;This program illustrates simple speed control

;and dynamic braking of a small DC motor using the SPice10213

;on an MS12 controller. It has a forward/reverse button and a stop/start button.

;Forward/reverse is active only while the motor is stopped.

;Stop/starts initial starts the speed ramping up. Once running,

;it triggers a sudden stop.

;The motor will be started at an initial PWM setting. This will be a function of

;the motor ... below some threshold it doesn't rotate at all.

InitialPWM      EQU     50

;The motor will ramp up to the PWM value MaxPWM

MaxPWM          EQU             200

iStopStart      iEQU            16              ;The Stop/Start button

iFwdRev         iEQU            14              ;The Forward/Reverse button

oDirection      oEQU            12              ;The direction relay

;------- RAM allocation ------------------

PWM             mEQU            0

;------

;Start of program

                setu            0,1             ;set spice connector pin 4 as DO

                setu            4,4             ;set spice connector pin 8 as PWM output

                SpiceConfigU

Idle:                   ;Back here when motor is stopped

                ResetK                                  :clear any pending buttons

IdleLoop:               ;Sit and wait for a button (command)

                GoIfInK         iStopStart,StartUp      ;Test for start command

                InputK          iFwdRev                 ;Test for reversal

                GoIfF           IdleLoop

                InputO          oDirection              ;Reverse the direction relay

                NOT

                Output          oDirection

                GoTo            IdleLoop

;Here to start the motor

StartUp:

                LoadX           InitialPWM              ;Initial speed

RampLoop:

                Push                                    ;Duplicate

                GoIfXEQ         MaxPWM,Running          ;Test if at max speed

                IncX                                    ;Increment the speed value

                Push                                    ;Duplicate

                AnOutJ                                  ;Update motor drive

                GoIfInK         iStopStart,Stop         ;Test for stop

                Pause           2                       ;20mS delay

                GoTo            RampLoop

Running:                ;Here when running at max speed

                WaitOnK         iStopStart              ;Wait for stop command

Stop:

                LoadX           0

                AnOutJ                                  ;Turn off PWM output

                InputO          oDirection              ;Reverse the direction relay ..

                NOT                                     ; .. for ...

                Output          oDirection              ;... dynamic braking

                Pause           10                      ;Time for motor to stop

                InputO          oDirection              ;Restore the direction relay

                NOT

                Output          oDirection

                GoTo            Idle


Example: Advanced motor control with SPice10213

This example provides sophisticated control. It implements speed-up and slow-down buttons, a stop/start button and a forward/reverse button. Unlike in the previous example, the direction button is honored at all times. If the motor is running it gets slowed to a stop, reversed and re-accelerated back up to speed. One simple code modification selects between gradual slow-down or sudden dynamic braking when the motor is to stop or reverse direction.

The program is written as a full multitasking application with 3 separate tasks. The programming techniques are fully explained in the multitasking tutorial. You will need to fully understand that material if this example is going to make sense to you.

The 3 tasks are:

Drive task

This task is responsible for controlling the output PWM in order to ramp the motor up or down to the desired speed.

Speed task

This task simply monitors the speed buttons and generates a target speed value.

Motor Sequencer task

This is the main control function which decides what is to be done. It primarily takes care of sequencing operations to take place in the right order whenever a button is pressed, irrespective of when it is pressed. The SPLatMap (state diagram) for the sequencer is described in the next section.

Previous Topic

Next Topic

In This Section

Sequencer SPLatMap

Program code


Sequencer SPLatMap

The diagram above is the SPLatMap for the main sequencer. It embodies logic that ensures that the whenever a button is pressed the system will respond in the most appropriate manner. It also provides the logic that allows the motor to reverse by slowing to a stop and then taking off in the opposite direction.

You should study this diagram until you understand it fully. If you don't recognize the symbols you should take our free online training course.

Please note: The interactive/online training course is very old. Everything in there is valid and worth learning, especially the underlying principles. However the course pre-dates significant developments, most notably our MultiTrack multitasking technology which makes SPLat programming even easier. So, if you are relatively new to controller programming, the course will give you a good foundation, but please don't think it represents the full SPLat story.

Some of the finer detail is left out, for clarity. For example, the diagram will only make sense if you understand that whenever the Forward/Reverse button is read, an internal direction flag (semaphore) is updated. Using a simple flag to track the user's desired direction significantly reduces the number of states required. The event "D=0" involve a subroutine call to the driver task to find out of the motor PWM is (yet) zero.

State 0

The motor is stationary. The F/R (Forward/Reverse) button is being monitored and the direction flag is being updated. If the S (Start/Stop) button is pressed the direction relay is set according to the direction flag and we switch to state 1. This logic prevent unnecessary switching of the relay.

State 1

This exists simply to ensure that the relay has completed its switching before cranking up the PWM. In the actual code the delay can be 10mS to 20mS, because the timing is generated synchronously with the timer ticks. The relay will in practice take about 3-5mS to switch.

After the delay we command the driver task to apply power to the motor. The driver will then take care of ramping up the PWM. How fast, and to what PWM value that is done, is of no concern to the sequencer.

State 2

This is the main state for the motor running. The sequencer is now only interested in the S button and the F/R button. If S is pressed the user wants the motor to decelerate and stop. If the F/R button is pressed the user wants the motor to decelerate, stop and then accelerate up to speed in the opposite direction. During either of those sequences the system must respond correctly to additional button presses.

Whichever button is pressed, the driver task is told to ramp down the motor speed to zero. In the S case we then go to state 3. In the F/R case we go to state 6.

State 3

In state 3 we are waiting for the driver task to reduce the PWM to 0. If the user hits the S button again we go back to state 2, sending the driver module a new command to accelerate back up to speed. If the user hits F/R, we respond by switching to state 5. If neither button is pressed the driver task will eventually report zero PWM (D=0). At that stage we go into a dynamic braking sequence by reversing the relay and switching to state 4.

State 4

This state times the dynamic braking stop time. Once it is over the system reverts to the idle state.

State 5

This is a "holding state" for F/R command received during deceleration to stop. Its primary function is to ensure that if S is hit before the motor has stopped, it is treated as a deferred start command to start the motor in the opposite direction when it eventually does stop. That is done by switching to state 6, which completes the decelerate, reverse and accelerate function. If the motor PWM actually hits 0 (D=0) while in state 5, the motor is dynamically braked at state 4. Note that "Rvrs Rly" reverses the relay from its actual position, not relative to the direction flag. If the user hits F/R in state 5 it effectively cancels out the F/R that brought us to state 5, so we go back to state 3.

State 6

This is the state we are in while decelerating prior to reversing and accelerating again. If the user hits F/R again, the sequence is abandoned by resuming the drive and going back to state 4. If the user hits S, state 8 takes over. If the PWM gets to zero the relay is set to the new direction (which will be the same as dynamic braking) and state 7 times a dynamic braking period.

State 7

This provides the timing for dynamic braking. At the end of the time the motor drive (acceleration) is commenced en route to state 2.

State 8

This acts as a holding state for S button presses. Basically toggling between state 6 and 8 determines what will happen when the PWM gets to 0.


Program code

The program code is reproduced below. You should master the Multitasking tutorial before attempting to understand this code!

;mdrv2.spt   Example motor drive program using SPice10213
;This program is written for an MMi99/200 with LCD, but can easily
;be ported to an MS12

;The program uses front panel push button inputs as follows:

;    Input  Function

;       12      + speed

;       11      - speed

;       10      Stop/Start

;        9      Forward/Reverse

;        8      Not used

;The program consists of 3 tasks:

;SPD    (Speed) Simply monitors the + and - buttons and generates a target speed value

;DRV    (Drive) Ramps the motor speed (PWM) towards the target speed

;MSQ    (Motor sequencer) Master sequencer for stop/start, forward/reverse. Uses DRV

;                       for actual PWM generation.

;The 3 tasks are written as independent tasks witin a multitasking structure.

;You need to understand the material in the multitasking tutorial to make sense of this

;=============== Constants ====================

SPD_Increment   EQU             10      ;Inc/Dec speed by this amount for each button push

StopTime        EQU             10      ; x 10mS. Time allowed for motor to stop with dynamic braking

;============== I/O allocations ==============

;Alter for MS12

;----------- Inputs ----------------

iSPeedUp        iEQU            12

iSpeedDn        iEQU            11

iStopStart      iEQU            10

iFwdRev         iEQU            9

;----------- Outputs ---------------

oDirection      oEQU            16

;========= RAM ==============

;-------- Semaphores (1-bit variables) in 1st 8 bytes ---------------

Direction       sEQU            0

DRV_Accelerate  sEQU            1

Tick10          sEQU            2

MSQ_DirFWD      sEQU            3

MSQ_RevFlag     sEQU            4

;-------- Other RAM ------------

MSQ_Addr        mEQU            20      ;2 bytes. Suspend address for MSQ

SPD_Target      mEQU            22      ;Target PWM

DRV_Addr        mEQU            23      ;2 bytes. Suspend address for DRV

DRV_PWM         mEQU            25      ;Current PWM value

SPD_Count       mEQU            26

MSQ_Count       mEQU            27

MSQ_State       mEQU            28

;-------------------------------------------------------------

Start:

                setu            0,1             ;set spice connector pin 4 as DO

                setu            3,1             ;set spice connector pin 7 as DO

                setu            4,4             ;set spice connector pin 8 as PWM output

                setu            5,4             ;set spice connector pin 9 as PWM output

                SpiceConfigU

                GoSub           SPD_Init        ;Initialize tasks

                GoSub           DRV_Init

                GoSub           MSQ_Init

MainLoop:

                GetTick10                       ;Generate 10mS heartbeat

                StoreS          Tick10

                GoSub           SPD_UpDate      ;Run task

                GoSub           DRV_UpDate      ;Run task

                GoSub           MSQ_UpDate      ;Run task

;Debug: Display the MSQ state number

                OBLCD_SetCur    1,13

                OBLCD_UDecDispMFW       MSQ_State

                GoTo            MainLoop

;====================================================================

;----------- Motor sequencer ---------------------

;Short name     MSQ

;Entry points

;               MSQ_Init        Initialize

;               MSQ_Update      Update

MSQ_UpDate:

                Resume          MSQ_Addr

MSQ_Init:

;State 0 is the idle state.

MSQ_Set0:

                SetMem          MSQ_State,0             ;For debugging monitor

MSQ_0:

                Suspend         MSQ_Addr

                GoIfInK         iStopStart,MSQ_Set1     ;Test stop/start button

                GoSub           MSQ_ReadFR              ;Test and process F/R

                GoTo            MSQ_0

;In state 1 we set the relay for the correct direction, then wait for it to settle

;prior to starting up the PWM drive

MSQ_Set1:

                SetMem          MSQ_State,1             ;For debugging monitor

                RecallS         MSQ_RevFlag             ;Set direction relay ...

                Output          oDirection              ;... according to direction flag

;Here we mark off two timer ticks to ensure at least a 10mS pause while the relay settles.

MSQ_1:

                Suspend         MSQ_Addr

                GoIfSF          Tick10,GenRet           ;Sync to timer tick

                Suspend         MSQ_Addr

                GoIfInK         iStopStart,MSQ_Set0     ;Test stop/start button

                GoIfSF          Tick10,GenRet           ;Wait for next tick

;State 2 is the run state. the DRV task may well be accelerating, but that does

;not concern us one way or the other here.

MSQ_Set2:

                SetMem          MSQ_State,2             ;For debugging monitor

                GoSub           DRV_Run                 ;Start ramping up speed

MSQ_2:

                Suspend         MSQ_Addr

                GoIfInK         iStopStart,MSQ_Set3     ;Test stop/start button

                GoSub           MSQ_ReadFR              ;Test and process F/R

                GoIfF           MSQ_2                   ;G/ not pressed

;State 6 is decellerating prior to reversing direction.

MSQ_Set6:

                SetMem          MSQ_State,6             ;For debugging monitor

                GoSub           DRV_Stop                ;Commence stop process

MSQ_6:

                Suspend         MSQ_Addr

                GoIfInK         iStopStart,MSQ_Set8     ;Test stop/start button

                GoSub           DRV_Get_ZPWM            ;Test if PWM is zero

                GoIfT           MSQ_Set7

                GoSub           MSQ_ReadFR              ;Test and process F/R

                GoIfF           MSQ_6                   ;G/ not pressed

                GoTo            MSQ_Set2

;State 3 is decellerating prior to stopping

MSQ_Set3:

                SetMem          MSQ_State,3             ;For debugging monitor

                GoSub           DRV_Stop

MSQ_3:

                Suspend         MSQ_Addr

                GoIfInK         iStopStart,MSQ_Set2     ;Test stop/start button

                GoSub           DRV_Get_ZPWM

                GoIfT           MSQ_Set4

                GoSub           MSQ_ReadFR              ;Test and process F/R

                GoIfF           MSQ_3                   ;G/ not pressed

;State 5 is if F/R is pushed while decellerating for a stop

MSQ_Set5:

                SetMem          MSQ_State,5             ;For debugging monitor

MSQ_5:

                Suspend         MSQ_Addr

                GoIfInK         iStopStart,MSQ_6        ;Test stop/start button

                GoSub           DRV_Get_ZPWM

                GoIfT           MSQ_Set4

                GoSub           MSQ_ReadFR              ;Test and process F/R

                GoIfF           MSQ_5                   ;G/ not pressed

                GoTo            MSQ_3

;State 8 is if Stop/start is hit while decelleration prior to reversing

MSQ_Set8:

                SetMem          MSQ_State,8             ;For debugging monitor

MSQ_8:

                Suspend         MSQ_Addr

                GoIfInK         iStopStart,MSQ_6        ;Test stop/start button

                GoSub           DRV_Get_ZPWM

                GoIfT           MSQ_Set4

                GoTo            MSQ_8

;State 4 is timing the dynamic braking interval after a stop.

MSQ_Set4:

                SetMem          MSQ_State,4             ;For debugging monitor

                InputO          oDirection              ;Read back direction relay

                NOT

                Output          oDirection

                SetMem          MSQ_Count,StopTime

MSQ_4:

                Suspend         MSQ_Addr

                GoSub           MSQ_ReadFR

                GoIfSF          Tick10,MSQ_4

                DMGNZ           MSQ_Count,MSQ_4

                GoTo            MSQ_Set0

;State 7 is timing the dynamic braking interval foir stopping during reversal

MSQ_Set7:

                SetMem          MSQ_State,7             ;For debugging monitor

                RecallS         MSQ_RevFlag

                Output          oDirection

                SetMem          MSQ_Count,StopTime

MSQ_7:

                Suspend         MSQ_Addr

                GoSub           MSQ_ReadFR

                GoIfSF          Tick10,MSQ_7

                DMGNZ           MSQ_Count,MSQ_7

                GoTo            MSQ_Set2

;----- Read and process F/R button. Return True if pressed

MSQ_ReadFR:

                InputK          iFwdRev

                Push

                RetIfF

                RecallS         MSQ_RevFlag

                NOT

                StoreS          MSQ_RevFlag

                Return

;----------- Drive Task --------------------------

;This task is responsible for ramping the PWM up and down to the target speed

;(when enabled). In essence it simply makes the actual PWM track the target PWM,

;with ramping. There is a coding option for sudden stop or smooth stop.

;Short name     DRV

;Uses lcd 1,0 through 1,3

;Entry points

;               DRV_Init        Initialize

;               DRV_Update      Update.

;               DRV_Run         Start ramping up. Simply updates DRV_Accelerate semaphore

;               DRV_Stop        Start ramping down. Simply updates DRV_Accelerate semaphore

;               DRV_Get_ZPWM    Return true if PWM=0

DRV_Get_ZPWM:

                Recall          DRV_PWM

                Push

                OR

                NOT

                Return

DRV_Update:

                Resume          DRV_Addr

DRV_Init:

DRV_Set0:

                SetMem          DRV_PWM,0

                GoSub           DRV_PWMOut

DRV_0:

                Suspend         DRV_Addr

                GoIfST          DRV_Accelerate,DRV_Set1 ;Test for RUN command

                GoTo            DRV_0

DRV_Set1:               ;Here on RUN command

                Suspend         DRV_Addr

;Which of the following 2 code lines determines whether the controller

;executes sudden stops, with dynamic braking, or smooth stop (ramping down the speed)

;Comment out the line you don't want.

;This line for sudden stop:

                GoIfSF          DRV_Accelerate,DRV_Set0 ;Test for STOP command

;This line for smooth stop:

                GoIfSF          DRV_Accelerate,DRV_Set2 ;Test for STOP command

                GoIfSF          Tick10,DRV_Set1         ;wait for time tick

                GoSub           SPD_GetSpeed            ;Target PWM

                Recall          DRV_PWM                 ;Actual PWM

                CompareR                                ;Compare Target to actual

                BranchR                                 ;Test result

                Target          DRV_Set1                ;On target

                Target          DRV_1_Dec               ;PWM too high

                Target          DRV_1_Inc               ;PWM too low

DRV_1_Dec:              ;Here to decrement PWM

                DecM            DRV_PWM

                GoSub           DRV_PWMOut

                GoTo            DRV_Set1

DRV_1_Inc:              ;Here to increment PWM

                IncM            DRV_PWM                 ;

                GoSub           DRV_PWMOut

                GoTo            DRV_Set1

DRV_Set2:               ;Here on stop command

                Suspend         DRV_Addr

                GoIfST          DRV_Accelerate,DRV_Set1 ;Test for RUN command

                GoIfSF          Tick10,DRV_Set2         ;wait for time tick

                GoIfMZ          DRV_PWM,DRV_Set0

                DecM            DRV_PWM

                GoSub           DRV_PWMOut

                GoTo            DRV_Set2

DRV_Run:

                SetS            DRV_Accelerate

                Return

DRV_Stop:

                ClrS            DRV_Accelerate

                Return

DRV_PWMOut:             ;Output the PWM

                Recall          DRV_PWM

                AnOutJ                          ;Alter for MS12

                OBLCD_SetCur    1,0

                OBLCD_UDecDispMFW       DRV_PWM

                Return

;----------- Speed task ---------------------------

;This task maintains the target speed.

;Short name     SPD

;Uses lcd 0,0 through 0,3

;Entry points

;               SPD_Init        Initialize

;               SPD_UpDate      Service push buttons, update target accordingly

;               SPD_Get_Speed   return current target speed in X

SPD_Init:

                Return

SPD_GetSpeed:

                Recall          SPD_Target

                Return

SPD_UpDate:

                GoIfInK         iSpeedUp,SPD_Up

                GoIfInK         iSpeedDn,SPD_Dn

GenRet: Return

SPD_Dn:

                SetMem          SPD_Count,SPD_Increment

SPD_DnA:

                GoIfMZ          SPD_Target,SPD_Show

                DecM            SPD_Target

                DMGNZ           SPD_Count,SPD_DnA

SPD_Show:

                OBLCD_SetCur    0,0

                OBLCD_UDecDispMFW       SPD_Target

                Return

SPD_Up:

                SetMem          SPD_Count,SPD_Increment

SPD_UpA:

                GoIfMEQ         SPD_Target,255,SPD_Show

                IncM            SPD_Target

                DMGNZ           SPD_Count,SPD_UpA

                GoTo            SPD_Show


SPice10214 - Potentiometer interface

Previous Topic

Next Topic

In This Chapter

SPice10214: Introduction

SPice10214: Fitting to controller

SPice10214: Power supply requirements

SPice10214: Connecting external potentiometers

SPice10214: Onboard potentiometer

SPice10214: Programming


SPice10214: Introduction

The SPice10214 lets you interface up to 3 potentiometers to a compatible SPLat controller board. It also contains one onboard potentiometer, giving you up to 4 "analog" settings. Typical uses would be "knob" setting of multiple timers or a temperature in an applications where a knob is easier for an operator that using buttons (digital) setting.

The SPice10214 is a Touch of SPice board. This is the name applied to one of the several expansion systems supported within the SPLat Expansion Framework. The board may be used with MMi99/MMi200, MS12 and SL99 controllers. It does not follow that future controllers will also support it ... the Touch of SPice system is a very low cost expansion scheme that does not guarantee compatibility across the whole family of SPLat controller products.


SPice10214: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. If you are using an MMi controller, remove the option link for input 13
  2. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  3. Identify the matching single in-line pin connector on the controller board.
  4. Experimentally plug the two boards together, taking care to align the connectors
  5. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  6. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  7. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice10214: Power supply requirements

The SPice10214 draws a tiny amount of power out of the SPice connector on the host board. This falls inside the power budget of the host board.


SPice10214: Connecting external potentiometers

The SPice10214 can handle 3 external potentiometers. The above picture shows one typical potentiometer attached to the P3 connector.

When the potentiometer is turned fully clockwise the wiper (purple wire) ends up at the "orange" end. In this position the board will return an analog reading at or very close to full scale. When the potentiometer is turned fully anti-clockwise the wiper (purple wire) ends up at the "brown" end. In this position the board will return an analog reading at or very close to full zero.

The P1 and P2 connectors work in exactly the same way.


SPice10214: Onboard potentiometer

The onboard potentiometer is the white and blue object with a screwdriver slot, at the top lefthand corner of the above picture. Turning it clockwise returns a maximum reading.


SPice10214: Programming

Once installed on a SPLat controller, the SPice10211 appears as several regular analog and digital I/O points. The following table shows the mappings for all the I/O on the board. Numbers in parenthesis are the numeric analog channel numbers used with fAnIn.

 | Cnctr pin | SLXXX | MMiXXX | MS12
P1 (Onboard) | 7 | AnInF (5) | AnInH (7) | AnInF (5)
P2 | 5 | AnInD (3) | AnInF (5) | AnInD (3)
P3 | 6 | AnInE (4) | AnInG (6) | AnInE (4)
P4 | 4 | AnInC (2) | AnInE (4) | AnInC (2)

initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10214 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice12014.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10214.

         ClrU
         setu         0,3         ;set spice connector pin 4 as analogue input
         setu         1,3         ;set spice connector pin 5 as analogue input
         setu         2,3         ;set spice connector pin 6 as analogue input
         setu         3,3         ;set spice connector pin 7 as analogue input
         SpiceConfigU

Once that is done programming is a simple matter of executing an analog input instruction against the input of interest.

The readings (values) obtained will depend on the potentiometer setting, and nominally range from 0 to 255. In practice you should allow for a few missing codes at each end of the scale due to potentiometer end resistances and inevitable errors in the analog inputs of the SPLat.

Do not rely on obtaining a reading of 0 or 255 for something mission critical to happen in your controller.

The best strategy is to work on an active range of 5 minimum and 245 maximum (but still tolerate 0 to 255 without doing something silly).

Example program

The following program illustrates how you can use the SPice10214 to create a variable timer.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

Example program:
;vartime.spt - SPice10214 demo program for MS12

;This program illustrates the use of a potentiometer to create

;a variable timer. It does the following:

;       1       Reads potentiometer on P3

;       2       Limits the range to 5-245

;       3       Shifts it to 0-240 and displays.

;       4       Scales to 1 to 5 seconds and displays

;       5       Generates an output pulse of 1 to 5 seconds

;               when a button is pressed.

        ON              20      ;LCD backlight

;Initialize the SPice connector for SPice10214

        ClrU

        setu            0,3     ;set spice connector pin 4 as analogue input

        setu            1,3     ;set spice connector pin 5 as analogue input

        setu            2,3     ;set spice connector pin 6 as analogue input

        setu            3,3     ;set spice connector pin 7 as analogue input

        SpiceConfigU

;----- Main loop ------

Loop:

        AnInE                           ;Read the pot

        GoSub           Fixup           ;Limit range to 5-245, shift to 0-240

        OBLCD_SetCur    1,0

        Push

        OBLCD_UDecDispXFW               ;Display the semi-processed reading

        float                           ;Convert to floating point

        fLoadQ          0.016667        ;Scale 0-240 to 0-4

        fMul

        fLoadQ          1               ;1s offset, giving the range 1 to 5

        fAdd

        OBLCD_fDispW    6,1             ;Display

        InputK          16

        GoSubIfT        PulseOut        ;Time duration is already in W

        GoTo            Loop            ;Repeat forever

;-------- Generate an output pulse equal to the value in W, in seconds.

PulseOut:

        fLoadQ          10

        fMul                            ; now in units of 100mS

        fSetTimer       0               ;Start timing

        On              8               ;Turn on the output

PO1:

        Test            0               ;Timed out

        GoIfT           PO1             ;g/ not yet

        Off             8               ;Turnoff the output

        Return                          ;All done

;=================================================

;Bound X within range 5 to 245, offset to 0 to 240

;I've made this a subroutine because it can then be re-used for all

;4 channels of the SPice10214.

Fixup:

        Push                            ;Make a copy

        GoIfXGE         5,L1            ;Test if < 5

        LoadX           5               ;Anything < 5 becomes 5

L1:

        Push                            ;Make a copy

        GoIfXLE         245,L2          ;Test if > 245

        LoadX           245             ;Anything > 245 becomes 245

L2:

        DecX                            ;Knock off 5 counts ...

        DecX                            ; ... reducing range...

        DecX                            ; ... 5 - 245 ...

        DecX                            ; ... to ...

        DecX                            ; ... 0 - 240

        Return


SPIce 10218 - 8 channel analog input multiplexer

The SPice10218 provides up to 8 analog channels on any compatible SPLat controller. It does this by multiplexing 8 inputs into one analog channel. It is set up for a fixed 0-10V input range. The resolution is determined by the SPLat controller - typically 10 bits in current models (MMi202, SL100, MS120), 8 bits in older models (MMi99-201, SL99, MS12).

Previous Topic

Next Topic

In This Chapter

SPice10218: Fitting to controller

SPice10218: Power supply requirements

SPice10218: Analog inputs

SPice10218: Programming


SPice10218: Fitting to controller

The board is very simple to fit to a SPLat controller. Simply follow these steps:

  1. If you are using an MMi controller, remove the option link for input 13
  2. Identify the single in-line female strip connector on the back side (non-component side) of the SPice board.
  3. Identify the matching single in-line pin connector on the controller board.
  4. Experimentally plug the two boards together, taking care to align the connectors
  5. Identify the single through mounting holes that line up on the two boards (approx 2.8mm dia)
  6. Use a tubular plastic spacer, machine screw (M2.5), nut and washers to hold the two boards together. Use either plastic hardware or suitable insulating washers under the nut and screw heads. The metal plating through the mounting holes, and the copper immediately around the holes, are at ground (0V) potential on both boards, and there is no need to insulate the screw from that metal. What you must watch out for is that no other circuit traces come in touch with a metal screw or nut.
  7. Double check that the mating connectors are lined up and that the screw or nut are not shorting out traces on the boards.


SPice10218: Power supply requirements

The SPice10218 derives its power from the main controller board's V+ supply. The current draw are depends on the V+ voltage, as follows:

V+ | Current
<12V | Negligible
15V | 6.6mA
18V | 13.2mA
24V | 25.5mA


SPice10218: Analog inputs

The SPice10218 has two 6-way terminal strips. Each strip has two 0V (ground, common) terminals and 4 terminals for numbered input channels. The input voltage range is 0 to +10V relative to the 0V terminals. You should therefore connect your input voltage between 0V (most negative wire) and a numbered terminal (positive wire).

These are screwless terminal blocks. To connect a wire, strip it and twist the strands, press down the plastic button on the terminal block, insert the wire and release the button. The buttons can be pressed with a small screwdriver.

Note that the inputs are NOT isolated. That means that the "negative" wire from your input device will be connected to the SPLat's 0V point, which is the same as the power supply 0V (common).


SPice10218: Programming

Once installed on a SPLat controller, the SPice10218 appears as one analog input and 3 digital outputs. The following table shows the mappings for all the I/O on the board. Numbers in parenthesis are the numeric analog channel numbers used with fAnIn. You should use fAnIn to get the full benefit of 10-bit resolution.

 | SPice cnctr pin | SLXXX | MMiXXX | MS12x
Analog input | 4 | AnInC (2) | AnInE (4) | AnInC (2)
Channel select 2 | 5 | DO 9 | DO 17 | DO 13
Channel select 1 | 6 | DO 10 | DO 18 | DO 14
Channel select 0 | 7 | DO 11 | DO 19 | DO 15

The SPice10218 is a multiplexer. That means it has the ability to select any one of its 8 analog input lines and route that through to a single analog input on the main SPLat controller board, where it can be read by an analog input instruction, e.g. fAnIn. The channel to be read is selected by sending a binary coded channel number to 3 "channel select" outputs. The analog input channel used, and the 3 channel select outputs, vary between SPLat controller boards according to the table above.

Channel select patterns

The following table defines the bit patterns for the 3 channel select outputs and the corresponding input channel numbers.

 | Channel select out
Ch | 0 | 1 | 2
0 | Off | Off | Off
1 | On | Off | Off
2 | Off | On | Off
3 | On | On | Off
4 | Off | Off | On
5 | On | Off | On
6 | Off | On | On
7 | On | On | On

You may recognize this as a simply binary count, which indeed it is. Note however that the the least significant channel select bit is in the most significant of the three outputs.

Initializing the SPice connector

Because the pins of the SPice connector, to which the SPice10218 is connected, can be configured for different functions (analog/digital, input/output), you need to have some code in your program that configures them to the correct settings for the SPice10218.

The following section of code should be run by your program just once, at the beginning, before your program attempts to do anything else with the SPice10218.

         ClrU
         setu         0,3         ;set spice connector pin 4 as analogue input
         setu         1,1         ;set spice connector pin 5 as digital output
         setu         2,1         ;set spice connector pin 6 as digital output
         setu         3,1         ;set spice connector pin 7 as digital output
         setu         4,0         ;set spice connector pin 8 as unchanged
         setu         5,0         ;set spice connector pin 9 as unchanged
         SpiceConfigU

Reading an analog value (example program)

Once the SPice connector has been initialized (which you would do early in your program using the code above), reading a channel requires setting the channel select outputs to the right pattern, allowing the analog voltage to settle, and then executing an analog input instruction.

The analog settling time must be taken into account. It cannot be ignored. The settling time constant is up about 0.5mS. It takes 7 time constants to settle to 10 bits. Hence the multiplexer requires at least 3.5mS to settle to 10 bits.

The following is a MultiTrack task that will automatically scan all 8 channels. To use it copy and paste the code into your program and edit the SP218_MUXStart and SP218_AnIn constant values to match your controller type. To start it up simply LaunchTask Scan218. To access a reading load X with the required channel number 0-7 and GoSub Get218Reading. The latest reading for the required channel will be returned in W as a normalized number 0-1.

* The following equates select the base address for the MUX select lines on the
* SPICE connector based on the board type:
*
* SL99, SL100                           SP218_MUXStart =  9, SP218_AnIn = 2
* MMi99, MMi200, MMi201, MMi202         SP218_MUXStart = 17, SP218_AnIn = 4
* MS12, MS120                           SP218_MUXStart = 13, SP218_AnIn = 2

SP218_MUXStart  equ     17      ;MMiXXX

SP218_AnIn      equ     4

Ch218           defBYTE

Readings218     defFLOAT        8       ;Result holding variables

;The scanner stores readings in an array of 8 floating point numbers.

;Because of the bit reversal that takes place on the 218 board, the

;order of readings in memory will be scrambled. This gets sorted out

;by bit reversing the index used during readout, in Get218Reading.

Scan218:

                Recall          Ch218

                LoadX           7

                OutputM         SP218_MUXStart

                Pause           2	         ;Guarantee 10mS minimum settling delay.

                fAnIn           SP218_AnIn

                Recall          Ch218

                Push

                XtoI

                IasJ:fStore     Readings218

                IncX

                LoadX           7

                AndM

                Store           Ch218

                GoTo            Scan218

;Return the latest reading for the channel number in X

Get218Reading:

                GoSub           ReverseBits012

                XtoI

                IasJ:fRecallW   Readings218

                Return

ReverseBits012: ;Reverse bit order 0, 1, 2 in X

RB_X1           defBYTE

RB_X2           defBYTE

RB_Y0           SetMem          RB_X2,0

                Store           RB_X1

                GoIfSF          0,RB_X1,RB_Y1

                SetS            2,RB_X2

RB_Y1           GoIfSF          1,RB_X1,RB_Y2

                SetS            1,RB_X2

RB_Y2           GoIfSF          2,RB_X1,RB_Y3

                SetS            0,RB_X2

RB_Y3           Recall          RB_X2

                Return


Product documentation: Xwire peripherals

Xwire is a versatile serial expansion bus introduced in 2007. It allows Xwire compatible controllers to be interconnected to form distributed processing control systems. It also supports special purpose peripheral boards. This chapter is dedicated to products sold as special purpose Xwire peripherals.

Xwire is documented here

Previous Topic

Next Topic

In This Chapter

SX10500: Dual K-type thermocouple board

SX10501: Ultrasonic distance measurement + Non-Contact Temperature Measurement (Optional)

SX10502/SX10502RH: On board Temperature and Humidity Measurement + Non-Contact Temperature Measurement (Optional)

SX10504: Water Level Detector + Analog I/O

SX10505: 4 analog out

SX10506: Pressure Sensor

SX10507: pH Measurement

SX10508: Xwire SD Logger

SX10509: Ethernet/Internet "SPLat Call Home"


SX10500: Dual K-type thermocouple board

  • Supports two K-type thermocouples
  • Interfaces to Xwire enabled controllers
  • Xwire and RS485 interfaces
  • 0°C to 1024°C range, 0.25°C resolution
  • Free Active Template driver program - you won't have to write any code to get readings
  • Built in adjustable low pass filtering for noise reduction
  • Credit card sized.

This board replaces the SPice10209. It uses our more recent Xwire serial peripheral bus instead of the SPice/TSP. Xwire is a newer development made possible by the availability of more capable processor chips. It is more versatile and easier to program than TSP, and allows several add-ons rather than just one.

The SX10500 gives you a choice of using the Xwire cabling for short cable runs between boards, or RS485 for long runs. Xwire is suitable for "inside the same box" use (a few feet) and has virtually zero hardware cost. RS485 is suitable for tens or hundreds of feet (depending on a number of factors) but requires an RS485 adaptor on the controller (whereas the SX10500 has RS485 built in, our standard controllers do not).

Previous Topic

Next Topic

In This Chapter

SX10500: Connecting to your controller

SX10500: Power supply requirements

SX10500: Thermocouple connections

SX10500: Selecting and setting address

SX10500: Programming


SX10500: Connecting to your controller

The board can connect to your controller in one of two ways: Via Xwire or via RS485. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol (language) that is used to convey data across the interface and wiring. The Xwire electrical interface is good for a few feet and up to 10 boards.

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 does not specify a protocol. When you use RS485 with this board it will use the Xwire protocol with up to 32 boards. The programming is the exact same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller.

Selecting the Xwire or RS485 interface

A 3-pin jumper in the middle of the board is labelled RS485 X. Inserting a jumper in the two pins closest to the camera will activate the RS485 interface. Inserting the jumper in the two pins furthest from the camera activates the Xwire electrical interface. The jumper must be correctly set or the board will not work as expected.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled X1 and X2. These are the Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisychaining, and are completely interchangeable. The lefthand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line terminator.

Xwire operates at 38.4KB, which is slow for RS485. The connection is "two wire, half duplex".


SX10500: Power supply requirements

The board will run off 10VDC to 24VDC. The voltage is allowed to go to a maximum of 32V.

Connect power to the 3-pin connector depicted above. The positive terminal is in the middle. There are 2 ground pins simply because we used a 3-pin connector to make it unique.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals (see Some common terminology) of all power supplies and boards must all be connected together or smoke will come out.


SX10500: Thermocouple connections

The two pairs of screw terminals labelled 0V/TH1 and 0V/TH2 are for connecting the thermocouples. You can only use K-type thermocouples with this board. The thermocouple manufacturer will tell you which lead is positive. The negative lead goes to 0V and the positive to TH1/TH2. If you are using only one channel use TH1.

Make sure that the thermocouple is fully insulated and has no connection to earth. Thermocouples work at extremely low voltage levels and readings are easily upset by any noise or hum.

There are a number of tricks of the trade when using thermocouples. Google it.


SX10500: Selecting and setting address

The board has a field of 4 jumpers designated "ID" and labelled 1, 2, 3, 4. Number 4 is reserved for future purposes, leaving three for addressing.

3 jumpers give 8 possible bus addresses. The bus address is the numeric address used by Xwire to select the board. Every board connected to an Xwire system (be it using the Xwire connections or RS485), must have a unique address. The addresses selectable for the SX10500 board are given in the following table. X means the indicated jumper is inserted.

1 | 2 | 3 | Addr
 |  |  | 64
X |  |  | 65
 | X |  | 66
X | X |  | 67
 |  | X | 68
X |  | X | 69
 | X | X | 70
X | X | X | 71

Don't attempt to use the number 4 jumper for address setting.

Under no circumstance put a jumper on the PGMLOAD pins unless instructed to do so. Those pins and the COMMS connector are used solely for factory programming of the board. This is not a SPLat controller and is not designed for customer programming.


SX10500: Programming

The SX10500 is very easy to program for because we have already done the hard work for you! The SX10500 Active Template lets you generate the code to drive your SX10500 board and integrate it into your program automatically.


SX10501: Ultrasonic distance measurement + Non-Contact Temperature Measurement (Optional)

  • Ultrasonic range finding range of 30cm - 600cm
  • Optional connection for TN9301 non contact temperature sensor
  • Interfaces to SPLat Xwire enabled controllers
  • Xwire and RS485 interfaces
  • Credit card sized.

The SX10501 has an ultrasonic sensor mounted to the underside of the board that measures the distance from the sensor to the nearest object directly in front of the ultrasonic sensor.

The SX10501 also comes with a connector that allows an optional TN9301 non-contact temperature sensor to be plugged in

The SX10501 gives you a choice of using the Xwire cabling for short cable runs between boards, or RS485 for long runs. Xwire is suitable for "inside the same box" use (a few feet) and has virtually zero hardware cost. RS485 is suitable for tens or hundreds of feet (depending on a number of factors) but requires an RS485 adaptor on the controller (whereas the SX10501 has RS485 built in, our standard controllers do not).

Previous Topic

Next Topic

In This Chapter

SX10501: Connecting to your controller

TN9301 peripheral non-contact temperature measurement.

SX10501: Power supply requirements

SX10501: Selecting and setting address

SX10501: Ultrasonic sensor

SX10501: Programming


SX10501: Connecting to your controller

The board can connect to your controller in one of two ways: Via the 2 pin TTL Xwire connector or via an RS485 adaptor, if your controller does not have an RS485 Xwire interface. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol that is used to convey data across the interface and wiring. The 2 pin TTL Xwire electrical interface is good for a few feet and up to 10 boards. The final choice of interface is dependent on the distance and the electrical environment (ie: the amount of electrical noise).

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 will allow up to 32 boards to be connected. The programming is the exactly same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller. The RS485 interface is more forgiving in terms of noise and is better protected against electrical transients.

Selecting the Xwire or RS485 interface

A 3-pin jumper in the middle of the board is labelled 'RS485 X'. Inserting a jumper in the two pins labelled RS485 will activate the RS485 interface. Inserting a jumper in the two pins labelled X will activate the Xwire electrical interface.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled X1 and X2. These are the TTL Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line terminator.

TN9301 interface

The TN9 connector provides an interface to an optional TN9301 peripheral board. The TN9301 provides 2 temperature readings. The first will be a ranged temperature reading of the target that the TN9301 is currently pointing at. The second will be the ambient temperature coming from the area immediately around the TN9301 itself.


TN9301 peripheral non-contact temperature measurement.

The TN9301 is an optional non contact temperature measurement device.

It allows the measurement of the temperature of an object that the TN9301 is pointing at and the ambient temperature reading in the area immediately around the TN9301 itself.

The TN9301 is a commercially available TN9 sensor with added wiring to match it to our SX10501.

Previous Topic

Next Topic

In This Chapter

TN9301 Specification

TN9301 Wiring Diagram


TN9301 Specification

The operating manual for the TN9301 can be found here: TN9301 manual.


TN9301 Wiring Diagram
Signal name | TN9 pin | Connector pin
Gnd | G, A | 1
Not used |  | 2
Data | D | 3
2 KHz Clock | C | 4
VCC | V | 5
Not used |  | 6

Length from the front of the module to the connector is 250mm.


SX10501: Power supply requirements

The board will run off 12VDC to 24VDC. Maximum current draw (TTL interface, RS485 termination link open) is 30mA. Using RS485 and 75 Ohms of termination, the maximum current draw is 90mA. The voltage is allowed to go to a maximum of 32V.

Connect power to the 3-pin connector depicted above. The positive terminal is in the middle. There are 2 ground pins simply because we used a 3-pin connector to make it unique.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals (see Some common terminology) of all power supplies and boards must all be connected together or smoke will come out.


SX10501: Selecting and setting address

The board has a field of 9 jumpers designated "ID" labelled 1-8 and MB. Jumper MB is reserved for future purposes, leaving 8 for addressing.Eight jumpers give 256 possible bus addresses, out of which addresses 254 and 255 are invalid. The bus address is the numeric address used by Xwire to select the board. Every board connected to an Xwire system (be it using the Xwire connections or RS485), must have a unique address. The jumpers are in a binary format meaning each jumper has a unique value. Any address value from 0 to 255 is possible by adding links to the jumper connector. Every link added adds that jumpers value to the final total.

Jumper | Value
JP1 | 1
JP2 | 2
JP3 | 4
JP4 | 8
JP5 | 16
JP6 | 32
JP7 | 64
JP8 | 128
MB | Reserved for future use. Not to be loaded.

Example: If there are 3 jumper links installed. JP1 was linked, JP5 was linked and JP6 was linked the address would be 49. (1 + 16 + 32 = 49)


SX10501: Ultrasonic sensor

The ultrasonic sensor has an operating temperature range -40 to 65 Degrees Celsius.

Detailed specifications can be found here: MaxSonar.


SX10501: Programming

The following tables show how the Xwire data block needs to appear in memory. Because the SX10501 can be used with or without an optional TN9301 peripheral board there are 2 sets of Xwire data blocks.

If the SX10501 is used only for the Ultrasonic distance measurement the Transmit data block will consist of 1 byte and the Receive data block will consist of 5 bytes.

However if the SX10501 is also using a TN9301 then the Transmit data block would consist of 2 bytes and the Receive data block would consist of 13 bytes.

Xwire transmit data block:

SX10501 only

Memory | Description | Format
0 | Master sequence number | Byte

Memory location 0 in the Xwire transmit data block corresponds to the 'Master sequence number'. The Master sequence number consists of a single byte in the range of 0 – 255. Whenever the controller software changes this number, the ultrasonic sensor will take a single reading. This allows several ultrasonic ranging modules to be used on the same controller and be sequenced to avoid interaction between them.

SX10501 with TN9301 attached

Memory | Description | Format
0; 1 | Master sequence number | Byte; Byte

To enable the use of a TN9301 peripheral board the 'Master sequence number' length is equated as 2 bytes size in the Xwire transmit data block. This second byte does not contain any data and the Master sequence number performs exactly as described above in the "SX10501 only" transmit data block. The 2nd byte invokes the TN9301 temperature measurement option.

Xwire receive data block:

SX10501 only

Memory | Description | Format
0; 1; 2; 3 | Distance | Floating point number returned in the range of 30cm – 600cm
4 | Returned sequence number | Byte returned in the range of 0 - 255

*Note that this data block does not need to start at memory location 0 and can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory locations 0-3 in the Xwire receive data block is the 'Distance' read back from the SX10501. The distance is returned in 4 bytes as a floating point number in the range of 30cm to 600cm. If the Ultrasonic sensor is operated outside of this range erroneous readings will result.

Memory location 4 in the Xwire receive data block is the 'Returned sequence number' from the SX10501. When the 'Returned sequence number' becomes the same value that is inside the 'Master sequence number' the distance measurement is complete and the data is ready to be taken.

SX10501 with TN9301 attached

Memory | Description | Format
0; 1; 2; 3 | Distance | Floating point number returned in the range of 30cm – 600cm
4 | Returned sequence number | Byte returned in the range of 0 - 255
5; 6; 7; 8 | Target temperature from the TN9301 | Floating point number returned in Degrees Celsius.
9; 10; 11; 12 | Ambient temperature from the TN9301 | Floating point number returned in Degrees Celsius.

*Note that this data block does not need to start at memory location 0 and can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory locations 5-8 in the Xwire receive data block is the remote Target temperature from the TN9301. This is the temperature of an object in front of the sensor. The temperature is returned as a floating point number in degrees Celsius. The temperature is updated at a rate of 1.4Hz.

Memory locations 9-12 in the Xwire receive data block is the Ambient temperature from the TN9301. The temperature is returned as a floating point number in degrees Celsius. The temperature is updated at a rate of 1.4Hz.

If the TN9301 is removed from the SX10501 or damaged an error value of NaN is returned from the SX10501 for the affected function.

Example Program 1

Interfaces with an SX10501 without TN9301 attachment.

Displays Distance in Centimetres and Inches updating 5 times a second.

********************* Xwire ****************************

DistMastSeqNum:         mEQU            218,1                   ;Define Xwire Tx Ram for slave

DistMastSeqNumLen:      EQU             1                       ;length of Tx Ram   *alternatively set to 2 if using TN9301 attachment

DistReadyFlag:          mEQU            224,1                   ;where the return sequence flag from the slave is stored

DistData:               mEQU            220,4                   ;Define Xwire Rx Ram for slave          * Distance

DistDataLen:            EQU             5                       ;length of Rx Ram   *alternatively set to 13 if using TN9301 attachment

DistAddress:            EQU             0                       ;Xwire Address of slave

;Initialisation:

                on              20                              ;turn on MS120 backlight

                XwireMaster     XwireTab                        ;start xwire going

                gosub           DSP_DO_Init                     ;initialise the Display handler

                gosub           XW_DO_Init                      ;Initialise the XWire handler

                runtasksforever

;***************  Display  OBJECT  **********************

;*****************  Description  ************************

;Short name:    DSP_

;

;Purpose        Display handler task.

;

;Behavior       *Initially displays a splash screen for 4 seconds.

;

;               *Displays Distance in Centimetres and Inches updating-

;                5 times a second.

******************* Entry points ************************

********************* Methods ***************************

* Entry point to initialise this task *

DSP_DO_Init:

                launchtask      DSP_Task                        ;initialise the task

                return

*********************************************************

******************  Display Task  ***********************

*********************************************************

DSP_Task:

DSP_Set0:

                                ;0123456789012345

                Oblcd_Cls

                Oblcd_SetCur    0,0

                Oblcd_Text      "  SX10501 Demo  "

DSP_0:

                pause           400

DSP_Set1:

DSP_1:

                yieldtask

                gosub           XW_GetDistance

                Oblcd_SetCur    0,0                             ;set curser to home position on line 1

                                ;0123456789012345               ;space available on lcd

                Oblcd_Text      "Distance        "

                Oblcd_SetCur    1,0                             ;set curser to home position on line 1

                                ;0123456789012345               ;space available on lcd

                Oblcd_fDispW    3,0                             ;display the distance

                Oblcd_Text              "cm   "

                gosub           DSP_ConvDist                    ;Convert Centimetres to Inches

                Oblcd_fDispW    3,0                             ;display the distance

                Oblcd_Text              "in   "

                goto            DSP_1

DSP_ConvDist:   ;will convert Centimetres to inches and return it in W

                floadQ          2.54

                fswap

                fdiv                                            ;distance in cm / 2.54 = inches. A -> W

                Return

;*****************  XWire Handler ***********************

;*****************  Description  ************************

;Short name:    XW_

;

;Purpose        Maintains and handles communications over XWire.

;

;Behavior

;

;               * The following task increments 'DistMastSeqNum' to request

;                 the current distance measurement from the slave.

;               * The slave responds when the measurement is ready to take

;                 by setting 'DistReadyFlag' to the same value in 'DistMastSeqNum'.

********************* Defines ***************************

;Define Memory

sXWireChange:   defsem                                          ;XWire index change semaphore

XWireIdx:       defbyte                                         ;index number of data to obtain

XW_Distance:    defbyte         4                               ;where distance data is stored in floating point

;Define Outputs

oXW_XWireError: oequ            9                               ;LED turns on when there is an Xwire communication error.

******************* Entry points ************************

********************* Methods ***************************

* Entry point to initialise this task *

XW_DO_Init:     launchtask      XW_Task                         ;put the XWire handler task in the task queue

                return

****************** Property Gets ************************

* Entry point to check Distance Ready *

XW_ChkDistRdy:

                Recalls         sXWireChange                    ;returns with data in X, if 0 then data ready for use

                return

* Entry point to retrieve Distance data *

XW_GetDistance:

                fRecallW        XW_Distance                     ;returns with data in W

                return

*********************************************************

******************  XWire Task  *************************

*********************************************************

XW_Task:

* XWire Distance data recovery *

XW_0:

                pause           20                              ;only update distance 5 times a second

                incm            DistMastSeqNum                  ;incrementing this memory makes slave measure distance and send it back

XW_1:           yieldtask                                       ;wait looking at that status byte

                xwiregeterrcount                                ;get the xwire error count

                goiff           XW_2

                on              oXW_XWireError                  ;turn on error LED if there are XWire errors

                pause           1

                goto            XW_3

XW_2:           off             oXW_XWireError

XW_3:           recall          DistMastSeqNum                  ;recall counter number currently in transmit mem

                recall          DistReadyFlag                   ;recall status byte, if same as counter then data ready to take

                compare

                goifnz          XW_1

                fRecallw        DistData                        ;recall the distance data returned via xwire

                fstore          XW_Distance                     ;move to storage memory location

                clrs            sXWireChange                    ;clear message changed semaphore so main knows to come get data

                goto            XW_0

*********************************************************

                NVEM0

XwireTab:

                NV0Byte         DistAddress,DistMastSeqNum,DistMastSeqNumLen,DistData,DistDataLen

                NV0Byte         255             ;end of table

Example Program 2

Interfaces with an SX10501 with TN9301 attachment.

Displays Ambient (A) and Target (T) temperature values in Celsius and Fahrenheit for 4 seconds.

Displays Distance in Centimetres and Inches for 4 seconds.

Repeats cycle.

********************* Xwire ****************************

DistMastSeqNum:         mEQU            218,1                   ;Define Xwire Tx Ram for slave

DistMastSeqNumLen:      EQU             2                       ;length of Tx Ram   *alternatively set to 1 if ONLY using Ultrasonic distance measurement

DistReadyFlag:          mEQU            224,1                   ;where the return sequence flag from the slave is stored

DistData:               mEQU            220,4                   ;Define Xwire Rx Ram for slave          * Distance

DistDataLen:            EQU             13                      ;length of Rx Ram   *alternatively set to 5 if ONLY using Ultrasonic distance measurement

DistAddress:            EQU             0                       ;Xwire Address of slave

TempData:               mEQU            225,8                   ;Define Xwire Rx Ram for slave          * Temperature (2 float, target/ambient temp)

;Initialisation:

                on              20                              ;turn on MS120 backlight

                XwireMaster     XwireTab                        ;start xwire going

                gosub           DSP_DO_Init                     ;initialise the Display handler

                gosub           XW_DO_Init                      ;Initialise the XWire handler

                runtasksforever

;***************  Display  OBJECT  **********************

;*****************  Description  ************************

;Short name:    DSP_

;

;Purpose        Display handler task.

;

;Behavior       *Initially displays a splash screen for 4 seconds.

;

;               *Displays Ambient (A) and Target (T) temperature values in

;                Celsius and Fahrenheit for 4 seconds.

;               *Displays Distance in Centimetres and Inches for 4 seconds.

;               *Repeats cycle.

******************* Entry points ************************

********************* Methods ***************************

* Entry point to initialise this task *

DSP_DO_Init:

                launchtask      DSP_Task                        ;initialise the task

                return

*********************************************************

******************  Display Task  ***********************

*********************************************************

DSP_Task:

DSP_Set0:

                                ;0123456789012345

                Oblcd_Cls

                Oblcd_SetCur    0,0

                Oblcd_Text      "  SX10501 Demo  "

                Oblcd_SetCur    1,0

                Oblcd_Text      "   with TN9301  "

DSP_0:

                pause           400

DSP_Set1:

                marktime

DSP_1:

                yieldtask

                gosub           XW_GetAmbientT                  ;get the ambient temperature

                Oblcd_SetCur    0,0

                Oblcd_Text      "A"

                Oblcd_fDispW    3,0                             ;display the temperature

                Oblcd_SpclChar  223                             ;display degree sign

                Oblcd_Text      "C  "

                gosub           XW_GetTargetT                   ;get the target temperature

                Oblcd_Text      "T"

                Oblcd_fDispW    3,0                             ;display the temperature

                Oblcd_SpclChar  223                             ;display degree sign

                Oblcd_Text      "C  "

                gosub           XW_GetAmbientT                  ;get the ambient temperature

                gosub           DSP_ConvTemp                    ;convert Celsius to Fahrenheit

                Oblcd_SetCur    1,0

                Oblcd_Text      "A"

                Oblcd_fDispW    3,0                             ;display the temperature

                Oblcd_SpclChar  223                             ;display degree sign

                Oblcd_Text      "F  "

                gosub           XW_GetTargetT                   ;get the target temperature

                gosub           DSP_ConvTemp                    ;convert Celsius to Fahrenheit

                Oblcd_Text      "T"

                Oblcd_fDispW    3,0                             ;display the temperature

                Oblcd_SpclChar  223                             ;display degree sign

                Oblcd_Text      "F  "

                loopiftiming    400,DSP_1                       ;Display temperatures for 4 seconds.

DSP_Set2:

                marktime

DSP_2:

                yieldtask

                gosub           XW_GetDistance

                Oblcd_SetCur    0,0                             ;set curser to home position on line 1

                                ;0123456789012345               ;space available on lcd

                Oblcd_Text      "Distance        "

                Oblcd_SetCur    1,0                             ;set curser to home position on line 1

                                ;0123456789012345               ;space available on lcd

                Oblcd_fDispW    3,0                             ;display the distance

                Oblcd_Text              "cm   "

                gosub           DSP_ConvDist                    ;Convert Centimetres to Inches

                Oblcd_fDispW    3,0                             ;display the distance

                Oblcd_Text              "in   "

                loopiftiming    400,DSP_2

                goto            DSP_Set1

DSP_ConvDist:   ;will convert Centimetres to inches and return it in W

                floadQ          2.54

                fswap

                fdiv                                            ;distance in cm / 2.54 = inches. A -> W

                Return

DSP_ConvTemp:   ;will convert Celsius to fahrenheit and return it in W.

                floadQ          1.8

                fmul

                floadQ          32

                fadd                                            ;temp in C x 1.8 + 32  = fahrenheit. A -> W

                Return

;*****************  XWire Handler ***********************

;*****************  Description  ************************

;Short name:    XW_

;

;Purpose        Maintains and handles communications over XWire.

;

;Behavior

;

;               * The following task increments 'DistMastSeqNum' to request

;                 the current distance measurement from the slave.

;               * The slave responds when the measurement is ready to take

;                 by setting 'DistReadyFlag' to the same value in 'DistMastSeqNum'.

;

;               * The Ambient and Target temperature values are constantly updated.

********************* Defines ***************************

;Define Memory

sXWireChange:   defsem                                          ;XWire index change semaphore

XWireIdx:       defbyte                                         ;index number of data to obtain

XW_Distance:    defbyte         4                               ;where distance data is stored in floating point

;Define Outputs

oXW_XWireError: oequ            9                               ;LED turns on when there is an Xwire communication error.

******************* Entry points ************************

********************* Methods ***************************

* Entry point to initialise this task *

XW_DO_Init:     launchtask      XW_Task                         ;put the XWire handler task in the task queue

                return

****************** Property Gets ************************

* Entry point to check Distance Ready *

XW_ChkDistRdy:

                Recalls         sXWireChange                    ;returns with data in X, if 0 then data ready for use

                return

* Entry point to retrieve Distance data *

XW_GetDistance:

                fRecallW        XW_Distance                     ;returns with data in W

                return

* Entry point to retrieve Target Temperature data *

XW_GetTargetT:

                fRecallW        TempData                        ;returns with data in W

                return

* Entry point to retrieve Ambient Temperature data *

XW_GetAmbientT:

                loadi           1

                ifRecallW       TempData                        ;returns with data in W

                return

*********************************************************

******************  XWire Task  *************************

*********************************************************

XW_Task:

* XWire Distance data recovery *

XW_0:

                pause           20                              ;only update distance 5 times a second

                incm            DistMastSeqNum                  ;incrementing this memory makes slave measure distance and send it back

XW_1:           yieldtask                                       ;wait looking at that status byte

                xwiregeterrcount                                ;get the xwire error count

                goiff           XW_2

                on              oXW_XWireError                  ;turn on error LED if there are XWire errors

                pause           1

                goto            XW_3

XW_2:           off             oXW_XWireError

XW_3:           recall          DistMastSeqNum                  ;recall counter number currently in transmit mem

                recall          DistReadyFlag                   ;recall status byte, if same as counter then data ready to take

                compare

                goifnz          XW_1

                fRecallw        DistData                        ;recall the distance data returned via xwire

                fstore          XW_Distance                     ;move to storage memory location

                clrs            sXWireChange                    ;clear message changed semaphore so main knows to come get data

                goto            XW_0

*********************************************************

                NVEM0

XwireTab:

                NV0Byte         DistAddress,DistMastSeqNum,DistMastSeqNumLen,DistData,DistDataLen

                NV0Byte         255             ;end of table


SX10502/SX10502RH: On board Temperature and Humidity Measurement + Non-Contact Temperature Measurement (Optional)

(SX10502RH Illustrated above)

  • On board temperature and humidity measurement (SX10502RH only)
  • Optional connection for TN9301 non contact temperature sensor
  • TN9301 can be mounted directly on the PCB or plugged in on flying wires
  • Interfaces to SPLat Xwire enabled controllers
  • Xwire TTL and RS485 interfaces
  • Credit card sized.

The SX10502 and SX10502RH give you a choice of using the Xwire TTL cabling for short cable runs between boards or RS485 for long runs. Xwire is suitable for "inside the same box" use (a few feet) and has virtually zero hardware cost. RS485 is suitable for tens or hundreds of feet (depending on a number of factors) but requires an RS485 adaptor on the controller (whereas the SX10502 has RS485 built in, our standard controllers do not).

Previous Topic

Next Topic

In This Chapter

SX10502/SX10502RH: Connecting to your controller

SX10502/SX10502RH: Power supply requirements

SX10502/SX10502RH: Selecting and setting address

SX10502RH: Onboard temperature/RH sensor

SX10502/SX10502RH: Programming


SX10502/SX10502RH: Connecting to your controller

The board can connect to your controller in one of two ways: Via the 2 pin TTL Xwire connector or via an RS485 adaptor, if your controller does not have an RS485 Xwire interface. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol that is used to convey data across the interface and wiring. The 2 pin TTL Xwire electrical interface is good for a few feet and up to 10 boards. The final choice of interface is dependent on the distance and the electrical environment (ie: the amount of electrical noise).

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 will allow up to 32 boards to be connected. The programming is the exactly same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller. The RS485 interface is more forgiving in terms of noise and is better protected against electrical transients.

Selecting the Xwire or RS485 interface

A 3-pin jumper in the middle of the board is labelled 'RS485 X'. Inserting a jumper in the two pins labelled RS485 will activate the RS485 interface. Inserting a jumper in the two pins labelled X will activate the TTL Xwire electrical interface.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled X1 and X2. These are the TTL Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line terminator.

TN9301 interface

The TN9 connector provides an interface to an optional TN9301 peripheral board. The TN9301 provides 2 temperature readings. The first will be a ranged temperature reading of the target that the TN9301 is currently pointing at. The second will be the ambient temperature coming from the area immediately around the TN9301 itself. Key specifications for the TN9301 can be found in the TN9301 Specification section of the SPLat Knowledge Base.


SX10502/SX10502RH: Power supply requirements

The board will run off 12VDC to 24VDC. Maximum current draw (TTL interface, RS485 termination link open) is 30mA. Using RS485 and 75 Ohms of termination, the maximum current draw is 90mA. The power supply voltage is allowed to go to an absolute maximum of 32V.

Connect power to the 3-pin 0.1" connector marked "POWER" as shown above. The positive terminal is in the middle. The two outer pins are ground.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals of all power supplies and boards must all be connected together or they may emit smoke.


SX10502/SX10502RH: Selecting and setting address

The board has a field of 9 jumpers designated "ID" labelled 1-8 and MB. Jumper MB is reserved for future purposes, leaving 8 for addressing. The address is the numeric address used by Xwire to select a particular board on the Xwire bus. Every board connected to an Xwire system (be it using the Xwire connections or RS485), must have a unique address. The jumpers are in a binary format meaning each jumper has a unique value. Any address value from 0 to 253 is possible by adding links to the jumper connector. Every link added adds that jumper's value to the final total.

Jumper | Value
JP1 | 1
JP2 | 2
JP3 | 4
JP4 | 8
JP5 | 16
JP6 | 32
JP7 | 64
JP8 | 128
MB | Reserved for future use. Not to be loaded.

Example: If 3 jumper links are installed, JP1, JP5 and JP6, the address would be 49. (1 + 16 + 32 = 49)


SX10502RH: Onboard temperature/RH sensor

The onboard temperature and RH sensor is a Sensirion model SHT71 device. You can find full specifications for the sensor at: http://www.sensirion.com/en/01_humidity_sensors/05_humidity_sensor_sht71.htm
Please familiarize yourself with that information. Note in particular that any humidity sensing element must necessarily be exposed to the ambient air, and can damaged by certain chemicals and fumes.


SX10502/SX10502RH: Programming

The SX10502 and SX10502RH have up to 4 temperature/humidity measuring functions depending on the model and whether or not there is an optional TN9301 peripheral board attached. Each of these functions is returned as a floating-point number. If it is a temperature function, the returned value will be in degrees Celsius. If it is humidity the returned value will be a percentage.

The remote target temperature and the ambient temperature from the TN9301 is updated at a rate of 1.4Hz. The on board temperature and humidity is updated at a rate of 2Hz.

Because the SX10502 has 2 model variations and can be used with or without an optional TN9301 sensor, the programmer is required to enable/disable selected functions of the SX10502.

By setting the Xwire Transmit Data length to 0 Bytes the Control Byte is disabled and ALL measurement functions will be enabled by default.

By setting the Xwire Transmit Data length to 1 Byte the Control Byte is enabled.

If the Control Byte is set to a number between 0-15 certain functions will be enabled.

If the TN9301 or on board sensor is removed from the SX10502 or damaged an error value of NaN is returned from the SX10502 for the affected function.

The following table depicts what functions will be enabled depending on the contents of the Control Byte, and the number of bytes needed for the Receive Data length. The relative location of the data returned from the functions inside the Xwire Receive Data block is also shown.

Xwire Receive Data block relative byte addresses
Control Byte | 0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15 | Receive Data length
0 | TN9301 Object Temperature | TN9301 Ambient Temperature | On board Temperature | On board Humidity | 16
1 | TN9301 Object Temperature |  |  |  | 4
2 | TN9301 Ambient Temperature |  |  |  | 4
3 | TN9301 Object Temperature | TN9301 Ambient Temperature |  |  | 8
4 | On board Temperature |  |  |  | 4
5 | TN9301 Object Temperature | On board Temperature |  |  | 8
6 | TN9301 Ambient Temperature | On board Temperature |  |  | 8
7 | TN9301 Object Temperature | TN9301 Ambient Temperature | On board Temperature |  | 12
8 | On board Humidity |  |  |  | 4
9 | TN9301 Object Temperature | On board Humidity |  |  | 8
10 | TN9301 Ambient Temperature | On board Humidity |  |  | 8
11 | TN9301 Object Temperature | TN9301 Ambient Temperature | On board Humidity |  | 12
12 | On board Temperature | On board Humidity |  |  | 8
13 | TN9301 Object Temperature | On board Temperature | On board Humidity |  | 12
14 | TN9301 Ambient Temperature | On board Temperature | On board Humidity |  | 12
15 | TN9301 Object Temperature | TN9301 Ambient Temperature | On board Temperature | On board Humidity | 16

*Note that this data block does not need to start at memory location 0 and can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Example Program 1

* Example program demonstrating how to interface with a SX10502 *
* For use with MMI202 and SX10502 *
* 2x16 LCD *

** Setup **

* The SX10502 is interfaced to the MMI202 over X-Wire protocol by setting the communications jumper to
* 	the X position.
* No board ID jumpers are installed as the X-Wire slave address for the SX10502 is address 0.
* If it is a MMI202 the X-Wire cable is connected from the Spice connector to the X1 connector on the SX10502.
* If it is a MMI202a the X-Wire cable is connected from the CN12 connector to the X1 connector on the SX10502.

** Program operation **

* X-Wire is set up to return data to the MMI from both temperature functions on the TN9301.

* Temperature from the TN9301 peripheral board are continually updated on the display.

* If at any time there are Xwire communications errors the front panel LED on output 15 will light.

* Equates *

* XWire Receive data block *
* Data block of 16 bytes *

RxRAMstart:		equ		100	;the starting memory location of the Xwire receive data block
RxRAMlength:		equ		8	;the size of the returned data in bytes depending on what functions are enabled
fTN9Temp:		mequ		100,4	;Location where the TN9301 object temperature is returned.
fTN9Amb:		mequ		104,4	;Location where the TN9301 ambient temperature is returned.

* XWire Transmit data block *
* Data block of 1 byte *

TxRAMlength:		equ		1	;setting the Transmit Ram length to 1 enables the control byte. 0 disables it.
bControlByte:		mequ		10	;location of the control byte to enable/disable SX10502 functions
SlaveAddress		equ		0	;the XWire slave address.

* Output Equates *

oXW_XWireError:		oequ		15	;LED turns on when there is an Xwire communication error.

 *******************
 ** Program start **
 *******************

Start
         XwireMaster 	XWireTab		;start xwire going
        setmem		bControlByte,3		;set the control byte to 3 to enable both TN9301 temperature functions.

        launchtask	LCD_Task		;start the Display task running
        launchtask	Xwire_Task		;start the XWire error checking task running
        runtasksforever

 ******************
 ** Display Task **
 ******************

LCD_Task:
        oblcd_CLS
               oblcd_Type      2                       ;set LCD to 2x16 type
LCD0:
        pause		50			;Update the display every 500mS
        oblcd_setcur	0,0
        ;		"0123456789012345"
        oblcd_text	"TN9 Temp: "
        frecallw	fTN9Temp
        oblcd_fdispw	4,1			;display the TN9301 object temperature with 1 decimal place
        oblcd_spclchar	223			;display a degree sign
        oblcd_text	"C"
        ;		"0123456789012345"
        oblcd_setcur	1,0
        oblcd_text	"TN9 Amb:  "
        frecallw	fTN9Amb
        oblcd_fdispw	4,1			;display the TN9301 ambient temperature with 1 decimal place
        oblcd_spclchar	223			;display a degree sign
        oblcd_text	"C"
        goto		LCD0

 ****************************
 ** Xwire Error Check Task **
 ****************************

Xwire_Task:

XW0:
        yieldtask
        xwiregeterrcount			;get the xwire error count
        goiff		XW1
        on		oXW_XWireError		;turn on error LED if there are XWire errors
        pause		1
        goto		XW0
XW1:		off		oXW_XWireError
        goto		XW0

 ** X-Wire Master table **

        NVEM0
XWireTab:
        NV0Byte         SlaveAddress,bControlByte,TxRAMlength,RxRAMstart,RxRAMlength

        NV0Byte         255    ;End of table sentinel.		;memory 10 (bControlByte) is transmit data block, 100 (RxRAMstart) is receive data block.
                                ;TxRAMlength of 1 byte enables bControlByte
                                ;RxRAMlength of 8 bytes for 2 floating point returned numbers.

Example Program 2

* Example program demonstrating how to interface with a SX10502RH *
* For use with MMI202 and SX10502RH *
* 2x16 LCD *

** Setup **

* The SX10502RH is interfaced to the MMI202 over X-Wire protocol by setting the communications jumper to
* 	the X position.
* No board ID jumpers are installed as the X-Wire slave address for the SX10502RH is address 0.
* If it is a MMI202 the X-Wire cable is connected from the Spice connector to the X1 connector on the SX10502RH.
* If it is a MMI202a the X-Wire cable is connected from the CN12 connector to the X1 connector on the SX10502RH.

** Program operation **

* X-Wire is set up to return data to the MMI from both temperature functions on the TN9301 and
*	the on board temperature and humidity.

* Temperature and humidity from the on board sensor are continually updated on the display.

* Pressing front panel button IN8 advances to the next screen.

* Temperature from the TN9301 peripheral board are continually updated on the display.

* If at any time there are Xwire communications errors the front panel LED on output 15 will light.

* Equates *

* XWire receive data block *
* Data block of 16 bytes *

RxRAMstart:		equ		100	;the starting memory location of the Xwire receive data block
RxRAMlength:		equ		16	;the size of the returned data in bytes depending on what functions are enabled
fTN9Temp:		mequ		100,4	;Location where the TN9301 object temperature is returned.
fTN9Amb:		mequ		104,4	;Location where the TN9301 ambient temperature is returned.
fOBTemp:		mequ		108,4	;Location where the on board temperature from the SX10502RH is returned.
fOBHumid:		mequ		112,4	;Location where the on board humidity from the SX10502RH is returned.

* XWire Transmit data block *
* Data block of 1 byte *

TxRAMlength:		equ		1	;setting the Transmit Ram length to 1 enables the control byte. 0 disables it.
bControlByte:		mequ		10	;location of the control byte to enable/disable SX10502RH functions
SlaveAddress		equ		0	;the XWire slave address.

* Output Equates *

oXW_XWireError:		oequ		15	;LED turns on when there is an Xwire communication error.

 *******************
 ** Program start **
 *******************

Start
         XwireMaster 	XWireTab		;start xwire going
        setmem		bControlByte,15		;set he control byte to 15 to enable all SX10502RH measurement functions

        launchtask	LCD_Task		;start the Display task running
        launchtask	Xwire_Task		;start the XWire error checking task running
        runtasksforever

 ******************
 ** Display Task **
 ******************

* Input equates *

iLCD_Cycle:	iequ		8			;this input is used to cycle the data on the LCD

LCD_Task:
        oblcd_CLS
               oblcd_Type      2                       ;set LCD to 2x16 type
LCD0:
        pause		50
        goifink		iLCD_Cycle,LCD1		;if toggle button is pressed change LCD display
        oblcd_setcur	0,0
        ;		"0123456789012345"
        oblcd_text	"OB Temp:  "
        frecallw	fOBTemp
        oblcd_fdispw	4,1			;display the on board temperature with 1 decimal place
        oblcd_spclchar	223			;display a degree sign
        oblcd_text	"C"
        ;		"0123456789012345"
        oblcd_setcur	1,0
        oblcd_text	"OB Humid: "
        frecallw	fOBHumid
        oblcd_fdispw	4,1			;display the on board humidity with 1 decimal place
        oblcd_text	" %"
        goto		LCD0
LCD1:
        pause		50
        goifink		iLCD_Cycle,LCD0		;if toggle button is pressed change LCD display
        oblcd_setcur	0,0
        ;		"0123456789012345"
        oblcd_text	"TN9 Temp: "
        frecallw	fTN9Temp
        oblcd_fdispw	4,1			;display the TN9301 object temperature with 1 decimal place
        oblcd_spclchar	223			;display a degree sign
        oblcd_text	"C"
        ;		"0123456789012345"
        oblcd_setcur	1,0
        oblcd_text	"TN9 Amb:  "
        frecallw	fTN9Amb
        oblcd_fdispw	4,1			;display the TN9301 ambient temperature with 1 decimal place
        oblcd_spclchar	223			;display a degree sign
        oblcd_text	"C"
        goto		LCD1

 ****************************
 ** Xwire Error Check Task **
 ****************************

Xwire_Task:

XW0:
        yieldtask
        xwiregeterrcount			;get the xwire error count
        goiff		XW1
        on		oXW_XWireError		;turn on error LED if there are XWire errors
        pause		1
        goto		XW0
XW1:		off		oXW_XWireError
        goto		XW0

 ** X-Wire Master table **

        NVEM0
XWireTab:
        NV0Byte         SlaveAddress,bControlByte,TxRAMlength,RxRAMstart,RxRAMlength

        NV0Byte         255    ;End of table sentinel.		;memory 10 (bControlByte) is transmit data block, 100 (RxRAMstart) is receive data block.
                                ;TxRAMlength of 1 byte enables bControlByte
                                ;RxRAMlength of 16 bytes for 4 floating point returned numbers.


SX10504: Water Level Detector + Analog I/O

  • Interfaces to Xwire enabled controllers
  • Xwire TTL and RS485 interfaces
  • Five channels of fluid level sensing.
  • Three 10 bit analog inputs.
  • Two 10 bit analog outputs.

The fluid level sensing works on the principle of detecting an electric current flow between two electrodes submersed in the fluid. Hence, it works on electrically conductive fluids such as water, milk, detergent etc. It will not work with non-conductive fluids like ultra-pure water or oils. AC excitation is used to minimise electroplating effects.

Previous Topic

Next Topic

In This Chapter

SX10504: Connecting to your controller

SX10504: Analogue channel jumper configuration

SX10504: Power supply requirements

SX10504: Selecting and setting address

SX10504: Programming

SX10504: About water level detection

SX10504: Example program


SX10504: Connecting to your controller

The board can connect to your controller in one of two ways: Via the 2 pin TTL Xwire connector or via an RS485 adaptor, if your controller does not have an RS485 Xwire interface. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol that is used to convey data across the interface and wiring. The 2 pin TTL Xwire electrical interface is good for a few feet and up to 10 boards. The final choice of interface is dependent on the distance and the electrical environment (ie: the amount of electrical noise).

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 will allow up to 32 boards to be connected. The programming is the exactly same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller. The RS485 interface is more forgiving in terms of noise and is better protected against electrical transients.

Selecting the Xwire or RS485 interface

A 3-pin jumper in the middle of the board is labelled 'RS485 X'. Inserting a jumper in the two pins labelled RS485 will activate the RS485 interface. Inserting a jumper in the two pins labelled X will activate the Xwire electrical interface.

Connecting to the Xwire interface

Two connectors on one side of the board are labelled X1 and X2. These are the TTL Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line terminator.

Connecting to the conductivity channels

The ten pin connector to the bottom right of the board is for connecting your electrodes. The channels are labelled C1-C5. There are two pins per channel, one for the active electrode (numbered with the channel number) and one for a ground (0V) connection. All 0V pins are connected together on the board and to the 0V power supply pin on the host controller board. The electrodes, and the use of the 0V connections, are explained in SX10504: About water level detection.

Sensitivity adjustment

The POT in the centre of the PCB is the sensitivity adjustment. It controls the sensitivity on all conductivity channels at the same time.

Analog inputs

The three 2-pin connectors at the bottom left of the board are the three 10 bit analog inputs. The analog input channels are labelled I0-I2. Associated with each analog channel is a set of four configuration pins. An input is configured by using one of the supplied jumpers to bridge certain pins on the "TIV"connector. Explained in detail in SX10504:Analog Jumper Configuration.

Analog outputs

The four pin connector to the bottom centre of the board is the two 10 bit analog outputs. The analog out channels are labelled O0 and O1. Each analog output has a voltage range of 0V – 10V, and can source a maximum load current of 5mA (2Kohm load).


SX10504: Analogue channel jumper configuration

The picture above shows the connector used for analog inputs, and the associated configuration pins. An input is configured by using one of the supplied jumpers to bridge certain pins.

There are three analog input channels on the SX10504 board. Each input can be configured to operate on 0-10V, 0-20mA or as a temperature measuring input (using an external thermistor sensor). In all cases the input uses one input pin and one common pin. The common pins on the analog input connector are tied to the board's 0V terminal. Please see the article on common terminology for an explanation of the 0V terminal.

The three analog inputs are designated as I1, I2 and I2. Each channel has a 4-pin configuration jumper field. A single jumper (shunt) on a pair of jumper pins selects the operating mode (V=voltage, I=current or T=temperature).

Using voltage mode

To use an analog input in voltage mode you set its jumper to bridge the two pins either side of the letter V and apply the input voltage between the input (positive) and the common (most negative). If Xwire has been successfully configured the analog channel data will appear in the memory locations of the Xwire receive data block as a normalized floating point number in W which ranges from 0 at 0V in to 1.000 at 10V in. The range 0 to 1.0 is divided into 1023 steps of 0.00097752.

In voltage mode the input resistance is 200KOhm. The accuracy is about 1.5% of full scale and resolution is 10-bit or 0.1% of full scale (8-bit/0.4% on older models). There is an input noise filter comprising a single RC time constant of 5mS.

If you leave the jumper out, you will get a 0-5V range instead of 0-10V.

Using current mode

To use an analog input in current mode you set its jumper to bridge the two pins either side of the letter I. You then feed the current to be measured into the input pin, with the return out of the common pin. The current source (e.g. 4-20mA transmitter) must be of the type that sources a positive current from its output pin and sinks (returns) the current into its ground (or minus out) pin. Most 4-20mA transmitters have an isolated output, in which case you connect the positive output of the transmitter to the analog input of the SX10504 and the negative output of the transmitter to the common pin on the SX10504.

If Xwire has been successfully configured the analog channel data will appear in the memory locations of the Xwire receive data block as a normalized floating point number in W which ranges from 0 at 0mA in to 1.000 at 20mA in. The range 0 to 1.0 is divided into 1023 steps of 0.00097752.

In current mode the input resistance is 250Ohms. There is an input noise filter comprising a single RC time constant of 10mS.

Accuracy and resolution (voltage and current modes)

The accuracy is about 1.5% of full scale. Resolution is 10-bit (0.1% of full scale).

Using thermistor mode

To use the analog input in temperature mode you set its jumper to bridge the two pins either side of the letter T and connect a suitable thermistor between the analog input pin and common. There are two basic types of thermistor: Positive Temperature Coefficient (PTC) and Negative Temperature Coefficient (NTC). The latter, NTC, is the type used for temperature measurement with SPLat. Typically, an NTC thermistor is specified as having a certain resistance at 25 degrees C.

The adjacent diagram shows the thermistor measuring circuit.

The thermistor is the bottom resistor in a voltage divider. The top resistor is a fixed on board 10.25KOhm resistor. The voltage divider is driven by a programmable voltage set by a floating point number stored to Xwire memory location 8-11 as a number 0-1. The thermistor drive voltage can be set to anything between 0V and 20V. Note that the board supply voltage must be at least 2V greater than the intended drive voltage. With this memory set to 0 it will result in a 0V drive, with this memory se to 1 it will result in 20V drive.

In this thermistor mode the full scale analog input voltage is 5V.


SX10504: Power supply requirements

The board will run off 12VDC to 24VDC. Maximum current draw (TTL interface, RS485 termination link open) is 30mA. Using RS485 and 75 Ohms of termination, the maximum current draw is 90mA. The voltage is allowed to go to a maximum of 32V.

Connect power to the 3-pin connector depicted above. The positive terminal is in the middle. There are 2 ground pins simply because we used a 3-pin connector to make it unique.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals of all power supplies and boards must all be connected together or smoke will come out.


SX10504: Selecting and setting address

The board has a field of 9 jumpers designated "ID" and labelled 1-9. Number 9 is reserved for future purposes, leaving 8 for addressing. Eight jumpers gives 256 possible bus addresses, out of which addresses 254 to 255 are invalid. The bus address is the numeric address used by Xwire to select the board. Every board connected to an Xwire system (be it using the Xwire connections or RS485), must have a unique address. The jumpers are in a binary format meaning each jumper has a unique value. Any address value from 0 to 255 is possible by adding links to the jumper connector. Every link added adds that jumpers value to the final total.

Jumper | Value
JP1 | 1
JP2 | 2
JP3 | 4
JP4 | 8
JP5 | 16
JP6 | 32
JP7 | 64
JP8 | 128
MB | Reserved for future use. Not to be loaded.

Example: If there are 3 jumper links installed. JP1 was linked, JP5 was linked and JP6 was linked the address would be 49. (1 + 16 + 32 = 49)


SX10504: Programming

The following tables depict how the Xwire data block needs to appear in memory.

Xwire receive data block:

Memory | Board designator Description | Format
0; 1; 2; 3 | I0; 10 bit analog in | Floating point number returned in the range of 0-1
4; 5; 6; 7 | I1; 10 bit analog in | Floating point number returned in the range of 0-1
8; 9; 10; 11 | I2; 10 bit analog in | Floating point number returned in the range of 0-1
12 | Conductivity channel data | Bits 0 –4 = Conductivity; Bits 5&6 unused; Bit 7 = Reading Valid

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together is in the order above and is defined in the NVEM Xwire table in the program code.

Memory locations in the Xwire receive data block 0-11 are 10 bit analog inputs from the SX10504. Each channel is returned in 4 bytes as a floating point number in the range of 0 to 1. The same format used by the fanin instruction.

Memory location 12 holds the conductivity channels data.

Bits 0-4 are the 5 conductivity channels. The bits are set true when the channel is conducting.

Bits 5&6 are unused.

Bit 7 is set true when the conductivity channels have had enough time to be Valid.
This is set following an enabling of the conductivity system from either the user setting the Conductivity enable or board power up. The timeframe for this is 4 times the conductivity Time factor.

Xwire transmit data block:

Memory | Board designator Description | Format
0; 1; 2; 3 | O0; 10 bit analog out. | Floating point number in the range of 0-1
4; 5; 6; 7 | O1; 10 bit analog out | Floating point number in the range of 0-1
8; 9; 10; 11 | Internal Thermistor drive. 8 bit analog out | Floating point number in the range of 0-1
12 | Conductivity control byte | Bits 0-6 = Time factor; Bit 7 = Conductivity enable

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together is in the order above and is defined in the NVEM Xwire table in the program code.

Memory locations in the Xwire transmit data block 0-7 correspond to the two 10 bit analog outputs on the SX10504. Each channel is sent in 4 bytes as a floating point number in the range of 0 to 1. The same format used by the fanout instruction.

Memory locations in the Xwire transmit data block 8-11 correspond to the 8 bit thermistor drive analog output.
This channel is sent 4 bytes as a floating point number in the range of 0 to 1.
With this memory set to 0 it will result in a 0V drive, with this memory se to 1 it will result in 20V drive.

Memory location 12 in the Xwire transmit data block is the conductivity control byte.

Using Conductivity channels

The conductivity of each of the 5 channels is tested with an AC excitation signal generated locally on each board. However if 2 or more boards are hooked up in the same tank/vat of liquid their excitation signals can interfere with one another and produce false readings. This can be overcome by only allowing only one board at a time to measure liquid levels. This is done by setting bit 7 of the conductivity control byte TRUE to completely turn off all conductivity channels on the alternative SX10504's.
By having one boards conductivity enabled at a time (by setting bit 7 of the conductivity control byte FALSE) it is possible to cycle through all the boards
in the shared tank/vat of liquid, thus we can have 10 boards all measuring different liquid levels but only 1 board will be testing conductivity at a time.

Bit 7 of the conductivity control byte is false by default.

Bits 0-6 of the conductivity control byte control the conductivity time factor.

The time factor is the time the conductivity channel must be stable before the SX10504 deems it to be conducting. This prevents false triggers due to the conductivity electrodes getting splashed and liquid sloshing around.

Equation:

Finding seconds: (Number + 1) x .05 = seconds

Finding number: (seconds / .05) - 1 = number

CCB | Value when set
Bit0 | 1
Bit1 | 2
Bit2 | 4
Bit3 | 8
Bit4 | 16
Bit5 | 32
Bit6 | 64

Example: If there are 3 bits set. Bit0 is set, Bit 2 is set and Bit 5 was set the total value is 37. (1 + 4 + 32 = 37)

Therefore (37 + 1) x .05 = 1.9 seconds.

Or, if working out the bits to set from the time you require.

(1.9 seconds / .05) – 1 = 37


SX10504: About water level detection

The SX10504 has five channels of water level switching. These work on the principle that if two electrodes have a voltage applied between them, and they are submersed in an electrically conductive fluid, a detectable current will flow. The amount of current that flows is proportional to the surface area of the electrodes (larger electrodes = more current) and inversely proportional to the distance between the electrodes (more spacing = less current). The current also depends on the conductivity of the fluid (very pure water = small current, very salty water = much more current). For a given electrode geometry and fluid the overall conduction can be defined as a resistance value.

The conductivity circuit on the SX10504 is designed to switch on and off at a certain resistance. The board has an adjustment for setting this resistance sensitivity. The adjustment range is approximately 2.2K to 27K. The circuit applies an AC voltage to the electrodes (DC can cause electrolysis problems).

Each of the six conductivity channels has a 2-wire connection, for 2 electrodes. On each channel one electrode is connected on the board to 0V or ground, while the other electrode is the "active" one. If all channels are used in a single tank to indicate up to 5 levels, then only one ground electrode is required. If you are monitoring several isolated (not interconnected) tanks, then each will require its own ground electrode.

It is important to realise that the grounds are all connected together and therefore at the same potential. The SX10504 senses the resistance between each active electrode and ground. If you were to try and have multiple ground electrodes all within the one fluid mass (say several interconnected tanks), the board could get confused. It can also get very confused if you have any other device in the fluid that generates AC or DC voltages or currents.

You have to design and provide your own electrodes. In the main this requires mainly common sense plus a bit of experimentation.

There are two main classes of electrode design, which are governed by the nature of the vessel or pipe work in which you want to detect the presence or absence of fluid. If the vessel is made of an electrically insulating material you will need two electrodes. If the vessel is made of metal, you will very possibly be able to use the vessel itself as one electrode. In the latter case you have to make sure the vessel is connected to the 0V (ground) electrode connection because the vessel will usually already be at ground potential.

It is usually advisable to use stainless steel for the electrodes. In many cases something as simple as two stainless steel machine screws through the side of a plastic vessel will do, with wires connected on the outside. In a metal vessel, a single screw with an insulated mounting arrangement, plus a wire from the screw and one from the body of the vessel (don't rely on ground conduction for the return path).

There are two parameters that affect the performance of your electrodes: How much they conduct when they are in fluid (wet) and how little they conduct when they are not in the fluid (dry).

How could dry electrodes conduct? Because they may not be dry! Wetting, residue build-up, even solid matter or foam, can all form unexpected and unwanted conduction paths when the electrodes are supposed to be dry. You need to apply some common sense to your own situation to come up with a suitable design that will avoid such effects.

You also need to apply some of that common sense to the wet situation.

Ask yourself:

Is there any residue in the fluid that might give the electrodes an insulating coating? (Greasy or oily water from a restaurant or garage could have this problem).

Can the conductivity vary a lot, so the tests you performed with tap water are invalidated when the customer uses distilled water?

When you are testing electrode geometries, it is a good idea to use a multimeter to measure the resistance between the electrodes. Providing you get a reasonably steady reading for a minute or two, this is quite valid (in some fluids electro-chemical effects may make a DC resistance reading meaningless).


SX10504: Example program

Example program demonstrating how to interface with a SX10504

* For use with MMI202 and SX10504 *
* 2x16 LCD *

** Setup **

* The SX10504 is interfaced to the MMI202 over X-Wire protocol by setting the communications jumper to
* 	the X position.
* No board ID jumpers are installed as the X-Wire address for the SX10504 is address 0.
* If it is a MMI202 the X-Wire cable is connected from the Spice connector on the MMI202 to the X1 connector on the SX10504.
* If it is a MMI202a the X-Wire cable is connected from the CN12 connector on the MMI202a to the X1 connector on the SX10504.
* 10k thermistors are connected to the analog inputs and the associated jumpers are set to the T position.

** Program operation **

* The thermistor drive voltage is set to 10 volts.

* Conductivity Channels.
* 	These can be triggered by causing conduction to ground.
* 	Input 0 on the MMI202 will toggle the Conductivity Control bit which will turn on and off all conductivity
*	channels.

* Pressing front panel button IN8 advances to the next screen.

* Analog Inputs.
*	From left to right fAI_AnIn0, fAI_AnIn1, fAI_AnIn2.
*	Input voltage is displayed for the analog inputs.

* Pressing front panel button IN8 advances to the next screen.

* Analog Outputs.
*	From left to right fAO_AnOut0, fAO_AnOut1.
*	Current output voltage is displayed for the analog outputs.
* 	Pressing front panel button IN12 increments AnOut0 by 1 volt.
* 	Pressing front panel button IN11 decrements AnOut0 by 1 volt.
* 	Pressing front panel button IN10 increments AnOut1 by 1 volt.
* 	Pressing front panel button IN9 decrements AnOut1 by 1 volt.
*	The voltage range is 0 to 10 Volts.

* If at any time there are Xwire communications errors the front panel LED on output 15 will light.

* Equates *

* XWire Receive data block *
* Data block of 13 bytes *

fAI_Anin0:		mequ		100,4	;Location where Analog Input 0 from SX10504 is returned.
fAI_Anin1:		mequ		104,4	;Location where Analog Input 1 from SX10504 is returned.
fAI_Anin2:		mequ		108,4	;Location where Analog Input 2 from SX10504 is returned.
bCC_Conduct_Data:	mequ		112	;Bits 0-4 conductivity channels (reads back true when conducting).
                         ;Bit 7 = readings available bit (reads back true when reading ready).

* XWire Transmit data block *
* Data block of 13 bytes *

fAO_Anout0:		mequ		10,4	;Location where Analog Output 0 value is stored for SX10504 to take.
fAO_Anout1:		mequ		14,4	;Location where Analog Output 1 value is stored for SX10504 to take.
bAI_Thermistor_Drive:	mequ		18,4	;Location where Thermistor Drive value is stored for SX10504 to take.
bCC_CCB:		mequ		22	;Bits 0-6 conductivity channels time factor.
                         ;Bit 7 disables ALL channels when set true.

* Input equates *

iCC_ConductToggle:	iequ		0	;this input is used to toggle the conductivity control bit, to force on/off conductivity testing.
iAO_AnOut0_Up_Btn:	iequ		12	;increments analog output 0 by 10%
iAO_AnOut0_Dn_Btn:	iequ		11	;decrements analog output 0 by 10%
iAO_AnOut1_Up_Btn:	iequ		10	;increments analog output 1 by 10%
iAO_AnOut1_Dn_Btn:	iequ		9	;decrements analog output 1 by 10%

* Output Equates *

oXW_XWireError:		oequ		15	;LED turns on when there is an Xwire communication error.

 *******************
 ** Program start **
 *******************

Start
         XwireMaster 	XWireTab		;start xwire going
        launchtask	Analog_Out_Task		;start the Analog Output task running
        launchtask	Analog_In_Task		;start the Analog Input task running
        launchtask	Conductivity_Task	;start the Conductivity Task running
        launchtask	LCD_Task		;start the Display task running
        launchtask	Xwire_Task		;start the XWire error checking task running
        runtasksforever

 ************************
 ** Analog Output Task **
 ************************

* Entry points *

AO_GET_AnOut0:
        frecallw	fAO_Anout0              ;recall user set analog out 0 voltage (returns value in W as a value 0-1)
        return
AO_GET_AnOut1:
        frecallw	fAO_Anout1              ;recall user set analog out 1 voltage (returns value in W as a value 0-1)
                return

Analog_Out_Task:

AO0:
        yieldtask
        goifink		iAO_AnOut0_Up_Btn,AO1	;does the user wish to increase the voltage on AnOut 0?
        goifink		iAO_AnOut0_Dn_Btn,AO3	;does the user wish to decrease the voltage on AnOut 0?

        goifink		iAO_AnOut1_Up_Btn,AO5	;does the user wish to increase the voltage on AnOut 1?
        goifink		iAO_AnOut1_Dn_Btn,AO7	;does the user wish to decrease the voltage on AnOut 1?
        goto		AO0

 ;; AnOut0 ;;

AO1:
        frecallq	fAO_Anout0		;limit check to prevent attempting to set voltage higher than maximum
        floadw		.9
        fsub
        fgoifpos	AO2

        frecallq	fAO_Anout0		;below upper limit so increment AnOut 0 value by 10%
        floadw		.1
        fadd
        fstore		fAO_Anout0
        goto		AO0
AO2:
        floadw		1			;reached limit so increment no further
        fstore		fAO_Anout0
        goto		AO0
AO3:
        frecallq	fAO_Anout0		;limit check to prevent attempting to set voltage lower than minimum
        floadw		.1
        fsub
        fgoifneg	AO4

        frecallq	fAO_Anout0		;above lower limit so decrement AnOut 0 value by 10%
        floadw		.1
        fsub
        fstore		fAO_Anout0
        goto		AO0
AO4:
        floadw		0			;reached limit so decrement no further
        fstore		fAO_Anout0
        goto		AO0

 ;; AnOut1 ;;

AO5:
        frecallq	fAO_Anout1		;limit check to prevent attempting to set voltage higher than maximum
        floadw		.9
        fsub
        fgoifpos	AO6

        frecallq	fAO_Anout1		;below upper limit so increment AnOut 0 value by 10%
        floadw		.1
        fadd
        fstore		fAO_Anout1
        goto		AO0
AO6:
        floadw		1			;reached limit so increment no further
        fstore		fAO_Anout1
        goto		AO0
AO7:
        frecallq	fAO_Anout1		;limit check to prevent attempting to set voltage lower than minimum
        floadw		.1
        fsub
        fgoifneg	AO8

        frecallq	fAO_Anout1		;above lower limit so decrement AnOut 0 value by 10%
        floadw		.1
        fsub
        fstore		fAO_Anout1
        goto		AO0
AO8:
        floadw		0			;reached limit so decrement no further
        fstore		fAO_Anout1
        goto		AO0

 ***********************
 ** Analog Input Task **
 ***********************

* Define Memory *

fAI_AnalogVal0:			defFloat		;Where the data from the returned analog reading is stored as a voltage.
fAI_AnalogVal1:			defFloat
fAI_AnalogVal2:			defFloat

* Entry points *

AI_GET_AnIn0:
        frecallw	fAI_AnalogVal0		;display task gets the analog input voltage value. (Return in W as value 0-5V)
        return
AI_GET_AnIn1:
        frecallw	fAI_AnalogVal1
        return
AI_GET_AnIn2:
        frecallw	fAI_AnalogVal2
        return

Analog_In_Task:
        floadw		.5			;set thermistor drive voltage to 10 volts
        fstore		bAI_Thermistor_Drive
        pause		200			;allow the drive voltage to settle
AI0:
        yieldtask
        frecallw	fAI_Anin0		;get the analog reading from AnIn channel 0.
        floadq		5			;load Q with maximum possible analog input voltage. (5 volts)
        fmul					;multiply to convert to volts.
        fstore 		fAI_AnalogVal0		;store calculation.

        frecallw	fAI_Anin1		;get the analog reading from AnIn channel 1.
        floadq		5			;load Q with maximum possible analog input voltage. (5 volts)
        fmul					;multiply to convert to volts.
        fstore 		fAI_AnalogVal1		;store calculation.

        frecallw	fAI_Anin2		;get the analog reading from AnIn channel 2.
        floadq		5			;load Q with maximum possible analog input voltage. (5 volts)
        fmul					;multiply to convert to volts.
        fstore 		fAI_AnalogVal2		;store calculation.
        goto		AI0

 *******************************
 ** Conductivity Channel Task **
 *******************************

* Define Memory *

bCC_Conduct_Ch1:	defbyte				;byte is true when channel is conducting and is false when it is not.
bCC_Conduct_Ch2:	defbyte
bCC_Conduct_Ch3:	defbyte
bCC_Conduct_Ch4:	defbyte
bCC_Conduct_Ch5:	defbyte

* Entry points *

CC_GET_CondCh1:
        recall		bCC_Conduct_Ch1		;returns a true in X if conducting and false if not conducting.
        return
CC_GET_CondCh2:
        recall		bCC_Conduct_Ch2
        return
CC_GET_CondCh3:
        recall		bCC_Conduct_Ch3
        return
CC_GET_CondCh4:
        recall		bCC_Conduct_Ch4
        return
CC_GET_CondCh5:
        recall		bCC_Conduct_Ch5
        return

Conductivity_Task:
                loadx		%00111011		;enable conductivity testing by clearing bit 7.
                            ;set the conductivity time factor by setting appropriate bits of 0-6. (59 + 1) x .05 = 3 seconds.
        store		bCC_CCB
CC0:
        yieldtask
        goifink		iCC_ConductToggle,CC1	;toggle the conductivity control bit to enable/disable conductivity channels.
        goto		CC3
CC1:
        recall		bCC_CCB
        loadx		%10000000
        andm					;separate the conductivity control 'bit' out of the Conductivity Control 'Byte'
        goift		CC2

        recall		bCC_CCB			;bit previously not set so OR %10000000 with CCB to set bit.
        loadx		%10000000
        orm
        store		bCC_CCB
        goto		CC3
CC2:
        recall		bCC_CCB			;bit previously was set so AND %01111111 with CCB to Clear bit.
        loadx		%01111111
        andm
        store		bCC_CCB
CC3:

 ;; Check if reading is valid ;;

        recall		bCC_Conduct_Data
        loadx		%10000000
        andm
        goiff		CC0			;if bit 7 is true then conductivity channel readings are stable and ready to be read.

 ;; Check conductivity channels ;;

        recall		bCC_Conduct_Data		;check conductivity channel 5
        loadx		%00010000		;mask all unwanted bits
        andm
        store		bCC_Conduct_Ch5		;store channel status

        recall		bCC_Conduct_Data		;check conductivity channel 4
        loadx		%00001000		;mask all unwanted bits
        andm
        store		bCC_Conduct_Ch4		;store channel status

        recall		bCC_Conduct_Data		;check conductivity channel 3
        loadx		%00000100		;mask all unwanted bits
        andm
        store		bCC_Conduct_Ch3		;store channel status

        recall		bCC_Conduct_Data		;check conductivity channel 2
        loadx		%00000010		;mask all unwanted bits
        andm
        store		bCC_Conduct_Ch2		;store channel status

        recall		bCC_Conduct_Data		;check conductivity channel 1
        loadx		%00000001		;mask all unwanted bits
        andm
        store		bCC_Conduct_Ch1		;store channel status
        goto		CC0

 ******************
 ** Display Task **
 ******************

* Input equates *

iLCD_Cycle:	iequ		8			;this input is used to cycle the data on the LCD

LCD_Task:
        oblcd_CLS

 ;; Display Conductivity channels ;;

LCD0:
                yieldtask
        goifink		iLCD_Cycle,LCD10	;if toggle button is pressed change LCD display
        oblcd_setcur	0,0
        ;		"0123456789012345"
        oblcd_text	"Chan: 1 2 3 4 5 "
        oblcd_setcur	1,0
        oblcd_text	"Cond: "
        gosub		CC_GET_CondCh1		;get channel 1 current conductivity status. Returned in X as true if conducting.
        goifF		LCD1
        oblcd_text	"Y "			;yes channel is conducting
        goto		LCD2
LCD1:
        oblcd_text	"N "			;no channel is not conducting
LCD2:
        gosub		CC_GET_CondCh2		;get channel 2 current conductivity status. Returned in X as true if conducting.
        goifF		LCD3
        oblcd_text	"Y "			;yes channel is conducting
        goto		LCD4
LCD3:
        oblcd_text	"N "			;no channel is not conducting
LCD4:
        gosub		CC_GET_CondCh3		;get channel 3 current conductivity status. Returned in X as true if conducting.
        goifF		LCD5
        oblcd_text	"Y "			;yes channel is conducting
        goto		LCD6
LCD5:
        oblcd_text	"N "			;no channel is not conducting
LCD6:
        gosub		CC_GET_CondCh4		;get channel 4 current conductivity status. Returned in X as true if conducting.
        goifF		LCD7
        oblcd_text	"Y "			;yes channel is conducting
        goto		LCD8
LCD7:
        oblcd_text	"N "			;no channel is not conducting
LCD8:
        gosub		CC_GET_CondCh5		;get channel 5 current conductivity status. Returned in X as true if conducting.
        goifF		LCD9
        oblcd_text	"Y "			;yes channel is conducting
        goto		LCD0
LCD9:
        oblcd_text	"N "			;no channel is not conducting
        goto		LCD0

 ;; Display Analog Inputs ;;

LCD10:
        yieldtask
        goifink		iLCD_Cycle,LCD_17	;if toggle button is pressed change LCD display
        oblcd_setcur	0,0
        ;		"0123456789012345"
        oblcd_text	"Analog Inputs:  "
        oblcd_setcur	1,0
        gosub		AI_GET_AnIn0

 ;; Check for LCD formatting issue ;;

                gosub           CF_Lower                ;check lower limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_11                  ;if returns TRUE check the upper limit

 ;; Analog In 0 ;;

             gosub	        AI_GET_AnIn0
                gosub           CF_Upper                ;check upper limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_11                  ;if returns TRUE run the LCD formatting issue workaround
            oblcd_text	"1.0"                   ;value to display is floating point value of 1, therefore use OBLCD_Text instruction to display 1.0
                goto            LCD_12
LCD_11:
        gosub		AI_GET_AnIn0
        oblcd_fdispw	3,1			;display AnIn channel 0 voltage
LCD_12:
        oblcd_text	"V "

 ;; Analog In 1 ;;

        gosub		AI_GET_AnIn1
                gosub           CF_Lower                ;check lower limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_13                  ;if returns TRUE check the upper limit
            gosub	        AI_GET_AnIn1
                gosub           CF_Upper                ;check upper limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_13                  ;if returns TRUE run the LCD formatting issue workaround
            oblcd_text	"1.0"                   ;value to display is floating point value of 1, therefore use OBLCD_Text instruction to display 1.0
                goto            LCD_14
LCD_13:
        gosub		AI_GET_AnIn1
        oblcd_fdispw    3,1			;display AnIn channel 1 voltage
LCD_14:
        oblcd_text	"V "

 ;; Analog In 2 ;;

        gosub		AI_GET_AnIn2
                gosub           CF_Lower                ;check lower limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_15                  ;if returns TRUE run the LCD formatting issue workaround
            gosub	        AI_GET_AnIn2
                gosub           CF_Upper                ;check upper limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_15                  ;if returns TRUE run the LCD formatting issue workaround
            oblcd_text	"1.0"                   ;value to display is floating point value of 1, therefore use OBLCD_Text instruction to display 1.0
                goto            LCD_16
LCD_15:
        gosub		AI_GET_AnIn2
        oblcd_fdispw    3,1			;display AnIn channel 2 voltage
LCD_16:
        oblcd_text	"V  "
        goto		LCD10

 ;; Display Analog Outputs ;;

LCD_17:
        yieldtask
        goifink		iLCD_Cycle,LCD0		;if toggle button is pressed change LCD display
        oblcd_setcur	0,0
        ;		"0123456789012345"
        oblcd_text	"Analog Outputs: "
        oblcd_setcur	1,0

 ;; Analog Out 0 ;;

            gosub           AO_Get_AnOut0		;recalls user set voltage in W as a value 0-1
        floadq		10     			;load Q with maximum possible output voltage
        fmul					;multiply to get user set voltage out

 ;; Check for LCD formatting issue ;;

                gosub           CF_Lower                ;check lower limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_18                  ;if returns TRUE check the upper limit
            gosub           AO_Get_AnOut0		;recalls user set voltage in W as a value 0-1
        floadq		10     			;load Q with maximum possible output voltage
        fmul					;multiply to get user set voltage out
                gosub           CF_Upper                ;check upper limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_18                  ;if returns TRUE run the LCD formatting issue workaround
            oblcd_text	"1.0"                   ;value to display is floating point value of 1, therefore use OBLCD_Text instruction to display 1.0
                goto            LCD_19
LCD_18:
            gosub           AO_Get_AnOut0		;recalls user set voltage in W as a value 0-1
        floadq		10     			;load Q with maximum possible output voltage
        fmul					;multiply to get user set voltage out
        oblcd_fdispw    3,1			;display AnOut channel 0 voltage
LCD_19:
        oblcd_text	"V "

 ;; Analog Out 1 ;;

            gosub           AO_Get_AnOut1		;recalls user set voltage in W as a value 0-1
        floadq		10     			;load Q with maximum possible output voltage
        fmul					;multiply to get user set voltage out

 ;; Check for LCD formatting issue ;;

                gosub           CF_Lower                ;check lower limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_20                  ;if returns TRUE check the upper limit
            gosub           AO_Get_AnOut1		;recalls user set voltage in W as a value 0-1
        floadq		10     			;load Q with maximum possible output voltage
        fmul					;multiply to get user set voltage out
                gosub           CF_Upper                ;check upper limit, FALSE will return in X if it is NOT a floating point value of 1
                goiff           LCD_20                  ;if returns TRUE run the LCD formatting issue workaround
            oblcd_text	"1.0"                   ;value to display is floating point value of 1, therefore use OBLCD_Text instruction to display 1.0
                goto            LCD_21
LCD_20:
            gosub           AO_Get_AnOut1		;recalls user set voltage in W as a value 0-1
        floadq		10     			;load Q with maximum possible output voltage
        fmul					;multiply to get user set voltage out
        oblcd_fdispw    3,1			;display AnOut channel 0 voltage
LCD_21:
        oblcd_text	"V      "
        goto		LCD_17

;; Subroutines ;;

 ;; Check Floating point value of 1 ;;

CF_Lower:                                               ;following subroutines test if LCD formatting workaround needs to be run.
                floadq          .95
                fsub
                fgoifpos        CF_0
        loadx           255                     ;Return with X TRUE if above lower limit check, this will cause an upper limit check.
        goto            CF_1
CF_0:
                loadx           0                       ;Return FALSE in X if no action is needed
CF_1:           return

CF_Upper:
                floadq          1.05
                fsub
                fgoifneg        CF_2
        loadx           255                     ;Return with X TRUE if both limit checks find the floating point number is 1 and-
        goto            CF_3                    ;LCD format workaround needs to be run
CF_2:
                loadx           0                       ;Return FALSE in X if no action is needed
CF_3:           return

 ****************************
 ** Xwire Error Check Task **
 ****************************

Xwire_Task:

XW0:
        yieldtask
        xwiregeterrcount			;get the xwire error count
        goiff		XW1
        on		oXW_XWireError		;turn on error LED if there are XWire errors
        pause		1
        goto		XW0
XW1:		off		oXW_XWireError
        goto		XW0

 ** X-Wire Master table **

        NVEM0
XWireTab:
        NV0Byte         0,10,13,100,13			;slave address 0, memory 10 is transmit data block, 13 bytes, 100 is Receive data block, 13 bytes.
        NV0Byte         255    ;End of table sentinel.


SX10505: 4 analog out

  • Interfaces to Xwire enabled controllers
  • Xwire TTL and RS485 interfaces
  • Two 10 bit analog outputs
  • Two 8 bit analog outputs
  • Credit card sized

Previous Topic

Next Topic

In This Chapter

SX10505: Connecting to your controller

SX10505: Power supply requirements

SX10505: Selecting and setting address

SX10505: Programming


SX10505: Connecting to your controller

The board can connect to your controller in one of two ways: Via the 2 pin TTL Xwire connector or via an RS485 adaptor, if your controller does not have an RS485 Xwire interface. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol that is used to convey data across the interface and wiring. The 2 pin TTL Xwire electrical interface is good for a few feet and up to 10 boards. The final choice of interface is dependent on the distance and the electrical environment (ie: the amount of electrical noise).

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 will allow up to 32 boards to be connected. The programming is the exactly same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller. The RS485 interface is more forgiving in terms of noise and is better protected against electrical transients.

Selecting the Xwire or RS485 interface

A 3-pin jumper in the middle of the board is labelled 'RS485 X'. Inserting a jumper in the two pins labelled RS485 will activate the RS485 interface. Inserting a jumper in the two pins labelled X will activate the Xwire electrical interface.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled X1 and X2. These are the TTL Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line terminator.

Analog Outputs

The 4 Pin connectors to the bottom left of the board are the four analog outputs. The analog output channels are labelled O0-O3. Channels O0 and O1 are 10 bit. Channels O2 and O3 are 8 bit. Each analog output has a voltage range of 0V – 10V, and can source a maximum load current of 5mA (2Kohm load).


SX10505: Power supply requirements

The board will run off 12VDC to 24VDC. Maximum current draw (TTL interface, RS485 termination link open) is 30mA. Using RS485 and 75 Ohms of termination, the maximum current draw is 90mA. The voltage is allowed to go to a maximum of 32V.

Connect power to the 3-pin connector depicted above. The positive terminal is in the middle. There are 2 ground pins simply because we used a 3-pin connector to make it unique.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals of all power supplies and boards must all be connected together or smoke will come out.


SX10505: Selecting and setting address

The board has a field of 9 jumpers designated "ID" and labelled 1-9. Number 9 is reserved for future purposes, leaving 8 for addressing. Eight jumpers give 256 possible bus addresses, out of which addresses 254 and 255 are invalid. The bus address is the numeric address used by Xwire to select the board. Every board connected to an Xwire system (be it using the Xwire connections or RS485), must have a unique address. The jumpers are in a binary format meaning each jumper has a unique value. Any address value from 0 to 255 is possible by adding links to the jumper connector. Every link added adds that jumpers value to the final total.

Jumper | Value
JP1 | 1
JP2 | 2
JP3 | 4
JP4 | 8
JP5 | 16
JP6 | 32
JP7 | 64
JP8 | 128
JP9 | Reserved for future use. Not to be loaded.

Example: If there are 3 jumper links installed. JP1 was linked, JP5 was linked and JP6 was linked the address would be 49. (1 + 16 + 32 = 49)


SX10505: Programming

The following table shows how the Xwire data block needs to appear in memory. The Transmit data block consists of 16 bytes.

Xwire transmit data block:

Memory | Description | Format
0; 1; 2; 3 | O0; 10 bit analog out. | Floating point number in the range of 0-1; 0V-10V output voltage range
4; 5; 6; 7 | O1; 10 bit analog out | Floating point number in the range of 0-1; 0V-10V output voltage range
8; 9; 10; 11 | O2; 8 bit analog out | Floating point number in the range of 0-1; 0V-10V output voltage range
12; 13; 14; 15 | O3; 8 bit analog out | Floating point number in the range of 0-1; 0V-10V output voltage range

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory locations in the Xwire transmit data block 0-7 correspond to the two 10 bit analog outputs on the SX10505.

Memory locations in the Xwire transmit data block 8-15 correspond to the two 8 bit analog outputs on the SX10505.

Each channel is sent in 4 bytes as a floating point number in the range of 0 to 1. The same format used by the "fanout" instruction.

Example program demonstrating how to interface with a SX10505

* The MMI202 communicates to the SX10505 over the XWire peripheral.
* MMI202 controller is the XWire master and the SX10505 is the XWire slave.
* The MMI202 has both its analog inputs set to Thermistor mode.
* The SX10505 has 4 analog output channels.
        * Channel O0 is connected to a fan set to run off 5 volts.

        * Channel O1 is connected to a fan set to run off 7.5 volts.

        * Channel O2 is connected to a pump which will echo analog input 2 on
        * the MMI202.

        * Channel O3 is connected to a pump which will output the inverse of analog
        * input 2 on the MMI202.

* If at any time there are Xwire communications errors OP15 on the MMI202 front panel will light.

* Equates *

oError:                 oequ            15      ;LED is lit when there is an XWire error.

* XWire Transmit data block *
* Data block of 16 bytes *

fSXAnout0:              mequ            10,4    ;Location where analog Output 0 value is stored for SX10505 to take.
fSXAnout1:              mequ            14,4    ;Location where analog Output 1 value is stored for SX10505 to take.
fSXAnout2:              mequ            18,4    ;Location where analog Output 2 value is stored for SX10505 to take.
fSXAnout3:              mequ            22,4    ;Location where analog Output 3 value is stored for SX10505 to take.

* Initialisation *

Start:
        XwireMaster     XWireTab                ;start xwire going

        floadw          1
        fanout          8                       ;set the MMI analog drive voltage

        floadw          .5                      ;analog output channel O0 set to 50%
        fstore          fSXAnout0

        floadw          .75                     ;analog output channel O1 set to 75%
        fstore          fSXAnout1
loop:
        pause           10                      ;only sample analog inputs every 100mS.
        fanin           2                       ;read analog channel 2 on the MMI, returns a number 0 - 1 in W. (0-100%)
        fstore          fSXAnout2

        fanin           3                       ;read analog channel 3 on the MMI, returns a number 0 - 1 in W. (0-100%)
        floadq          1
        fsub                                    ;invert the analog sample.
        fstore          fSXAnout3

        xwiregeterrcount                        ;get the xwire error count
        output          oError                  ;if the error count is above 1 led 15 on the MMI202 will light until next run
        goto            loop

        NVEM0
XWireTab:
        NV0Byte         0,10,16,0,0
        NV0Byte         255                     ;End of table sentinel.


SX10506: Pressure Sensor

  • On board pressure measurement
  • Interfaces to SPLat Xwire enabled controllers
  • Xwire TTL and RS485 interfaces
  • Credit card sized.

Previous Topic

Next Topic

In This Chapter

SX10506: General

SX10506: Specifications

SX10506: Connecting to your controller

SX10506: Selecting and setting address

SX10506: Usage Notes

SX10506: Programming


SX10506: General

The SX-10506 Xwire peripheral board interfaces a pressure sensor to a SPLat controller. As the sensors are dual port, they can measure positive pressures, vacuum and pressure differential. Typical applications would include water level sensing, clogged filter detection, air pressure regulation etc. The particular pressure sensor type used allows for different pressure ranges of:

Sensor Type Number | Pressure Range
MPX5010DP | 0 – 1.45 PSI, 0 - 10kPa, 1 metre H20
MPX5050DP | 0 – 7.25 PSI, 0 - 50kPa, 5 metres H20
MPX5100DP | 0 – 14.5 PSI, 0 - 100kPa, 10 metres H20
MPX5500DP | 0 – 72.5 PSI, 0 - 500kPa
MPX5700DP | 0 – 101.5 PSI, 0 – 700kPa

The SX-10506 is normally supplied with an MPX5100DP sensor, unless otherwise specified. Other types may incur a lead time.


SX10506: Specifications

Supply voltage range | 10 –24 | Volts DC
Supply current | 30 | mA (12/24 VDC)
Xwire interface | TTL and RS485 | Pin Jumper selectable
Xwire address range | 0 – 249 | Pin Jumper selectable
Xwire pressure data | 24 bit floating point number normalised to 1.0000 |


SX10506: Connecting to your controller

The board can connect to your controller in one of two ways: Via the 2 pin TTL Xwire connector or via an RS485 adaptor, if your controller does not have an RS485 Xwire interface. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol that is used to convey data across the interface and wiring. The 2 pin TTL Xwire electrical interface is good for a few feet and up to 10 boards. The final choice of interface is dependent on the distance and the electrical environment (ie: the amount of electrical noise).

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 will allow up to 32 boards to be connected. The programming is exactly the same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller. The RS485 interface is a lot more forgiving in terms of noise and is better protected against electrical transients and ground loop problems.

Selecting the Xwire or RS485 interface

A 3-pin jumper on the board is labelled 'RS485 X'. Inserting a jumper in the two pins labelled RS485 will activate the RS485 interface. Inserting a jumper in the two pins labelled X will activate the Xwire electrical interface.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled X1 and X2. These are the TTL Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line terminator.

Connecting Power

Connect power to the 3-pin connector depicted above. The positive terminal is in the middle. There are 2 ground pins simply because we used a 3-pin connector to make it unique.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals of all power supplies and boards must all be connected together or smoke will come out.


SX10506: Selecting and setting address

The board has a field of 9 jumpers designated "ID" and labelled 1-9. Number 9 is reserved for future purposes, leaving 8 for addressing. Eight jumpers give 256 possible bus addresses, out of which addresses 250 to 255 are invalid. The bus address is the numeric address used by Xwire to select the board. Every board connected to an Xwire system (be it using the Xwire connections or RS485), must have a unique address. The jumpers are in a binary format meaning each jumper has a unique value. Any address value from 0 to 255 is possible by adding links to the jumper connector. Every link added adds that jumpers value to the final total.

Jumper | Value
JP1 | 1
JP2 | 2
JP3 | 4
JP4 | 8
JP5 | 16
JP6 | 32
JP7 | 64
JP8 | 128
JP9 | Reserved for future use. Not to be loaded.

Example: If there are 3 jumper links installed. JP1 was linked, JP5 was linked and JP6 was linked the address would be 49. (1 + 16 + 32 = 49)


SX10506: Usage Notes

The sensors that can be used with this board all contain signal conditioning and temperature compensation. The final calibration of a system using the SX-10506 must be done as part of the application set up. As an example, say we wish to measure the height of water in a tank to a maximum height of 8 metres. Using an SX10506 loaded with a 14.5 PSI (100kPa, 10 metres H20) sensor, we need to calibrate the system to so that the sensing system reads zero when there is no water and 1 metre when the water head is at a height of 1 metre.

To do this, we need to take a reading with no water present (Poffset). This becomes the offset of the system. Then we take a reading with 1 metre of water (Pcal_1m). The span of the pressure measurement system is Pcal_1m – Poffset for 1 metre of water. The pressure for a given sensor reading is then:

Water height = (PressureSensorReading – Poffset) / (Pcal_1m – Poffset)

If during calibration, the reading with no water was 0.1107 (Poffset) and the reading with 1 metre of water was 0.1956 Pcal_1m, then a sensor reading of 0.6334 would give a water height of:

(0.6334 – 0.1107) / (0.1956 – 0.1107) = 6.16 metres

In practice it is always better to calibrate the system at the maximum pressure (more span), but this is not always practical. If we did calibrate at 8 metres, the equation would look like this:

Water height = (PressureSensorReading – Poffset) / ((Pcal_8m – Poffset) / 8)

Typically in your application program, Pcal and Poffset would be stored in non volatile memory once the calibration procedure had been done. Typically this procedure would be

1). Some key sequence to get the controller into the mode for pressure calibration

2). With no pressure (or water) press a button – controller notes the reading Poffset

3). Pressurise to some level and note the Pcal reading.

4). Save these parameters to non volatile memory

By knowing what height of water or pressure was used during the calibration procedure, the above equation can be used.


SX10506: Programming

The following table shows how the Xwire data block needs to appear in memory. The Receive data block consists of 4 bytes.

Xwire receive data block:

Memory | Description | Format
0; 1; 2; 3 | Pressure data | 24 bit floating point number normalised to 1.0000

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Example program demonstrating how to interface with a SX10506

*    Program written for a MMI202a with a 2x16 LCD.

*    The MMI202a communicates to the SX10506 over the XWire peripheral interface.

*    MMI202a controller is the XWire master and the SX10506 is the XWire slave.

** Setup **

* The SX10502 is interfaced to the MMI202a over X-Wire protocol by setting the communications jumper to the X position.

* No board ID jumpers are installed as the X-Wire slave address for the SX10506 is address 0.

* The X-Wire cable is connected from the CN12 connector on the MMI to the X1 connector on the SX10506.

** Program operation **

* X-Wire is set up to return pressure data to the MMI202a.

* Pressure from the SX10506 is continually updated on the LCD.

* If at any time there are Xwire communications errors the front panel LED on output 15 will light.

* The user presses and holds iBTN_Calibrate for 10 seconds to enter calibration mode.

* At power on if the pressure calibration has not been previously set the program will enter calibration mode.

* Once calibrated, the calibration values are stored to shadow memory so they survive power off.

   * XWire Equates *

XW_Address:       EQU            0                    ;Xwire Address of slave.

   * XWire Receive data block *

   * Data block of 4 bytes *

XW_RxRAMstart:    EQU            100                  ;The starting memory location of the Xwire receive data block.

XW_RxRAMlength:   EQU            4                    ;The size of the returned data in bytes.

XW_fPressure:     mEQU           100,4                ;Location where the SX10506 Pressure reading is returned.

   * XWire Transmit data block *

   * Data block of 0 bytes *

XW_TxRAMstart:    EQU            120                  ;The starting memory location of the xwire transmit data block.

XW_TxRAMlength:   EQU            0                    ;There are no bytes to send to the SX10506.

   * Input Equates *

iBTN_Calibrate:   iEQU           12                   ;User presses and holds for 10 seconds to enter calibration mode.

iBTN_Poffset:     iEQU           11                   ;When button is pressed the current pressure reading is saved as the offset. (Calibration only)

iBTN_Pspan:       iEQU           10                   ;When button is pressed the span is calculated and saved.                    (Calibration only)

   * Output Equates *

oXW_XWireError:   oEQU           15                   ;LED turns on when there is an Xwire communication error.

   * Pressure sensor calibration constants SHADOW memory *

CalFlag:          EQU            'AA                  ;Value of the valid calibration flag.

ShadowLength:     EQU            9                    ;Length of shadow memory.

ShadowStart:      EQU            200                  ;Start of shadow memory buffer.

fPoffset:         mEQU           200,4                ;Floating point pressure calibration offset value.

fPspan:           mEQU           204,4                ;Floating point pressure scale factor.

bCalibratedFlag:  mEQU           208                  ;Byte that is set to 'AA when the pressure sensor is calibrated.

   * defFOAT Directives *

fCalc:            defFLOAT                            ;Where the current calculated water height is stored.

 *******************

 ** Program start **

 *******************

   XwireMaster    XwireTab                            ;Start xwire going.

   LaunchTask     MSQ_Start                           ;Start the Main Sequencer task running.

   LaunchTask     Xwire_Task                          ;Start the XWire error checking task running.

   RunTasksForever

MSQ_Start:

   ShadowRead     ShadowStart,ShadowLength            ;Recall calibration data (if present) from shadow memory.

   GoIfMNE        bCalibratedFlag,CalFlag,Calibrate   ;If the calibration flag is 'AA then calibration is already set. Otherwise calibrate the pressure sensor.

MSQ_Display:

   YieldTask

   GoSub          MSQ_Sub0_CalcHeight                 ;Calculate the current water level.

   GoSub          MSQ_Sub1_DispHeight                 ;Display current water level.

   GoIfInOFF      iBTN_Calibrate,MSQ_Display          ;Test if the user wishes to enter Calibration mode.

   MarkTime                                           ;Start the timer running.

 * Enter this state if the user has pressed and holds *

 * the iBTN_Calibrate button *

MSQ_Display_T:

   YieldTask

   GoSub          MSQ_Sub0_CalcHeight                 ;Calculate the current water level.

   GoSub          MSQ_Sub1_DispHeight                 ;Display current water level.

   GoIfInOFF      iBTN_Calibrate,MSQ_Display          ;Is the user still holding the iBTN_Calibrate button ?

   LoopIfTiming   1000,MSQ_Display_T

   GoTo           Calibrate                           ;If the user holds the iBTN_Calibrate button for 10 seconds enter Calibration Mode.

 * Subroutines *

MSQ_Sub0_CalcHeight:

   fRecallQ       XW_fPressure                        ;Calculate water height. Water height = (PressureSensorReading - Poffset) / (Pspan)

   fRecallW       fPoffset

   fSub

   fRecallQ       fPspan

   fSwap

   fDiv

   fStore         fCalc                               ;Store the calculated value temporarily.

   Return

MSQ_Sub1_DispHeight:

   fRecallW       fCalc                               ;Recall value to display.

                  ;0123456789012345

   OBLCD_SetCur   0,0

   OBLCD_Text     "  Water height  "

   OBLCD_SetCur   1,0                                 ;Display result on the LCD.

   OBLCD_fDispW   7,2

   OBLCD_Text     "m        "

   Return

*******************************

* Pressure sensor calibration *

*******************************

* We are now in pressure calibration mode. We now wait for iBTN_Poffset button to be pressed before

* we store the low point value. Water depth is assumed to be at minimum.

Calibrate:        ;0123456789012345

   OBLCD_SetCur   0,0

   OBLCD_Text     "Water level = 0m"

   OBLCD_SetCur   1,0                                 ;Display message.

   OBLCD_Text     "Press Poffset   "

   WaitOn         iBTN_Poffset                        ;Has iBTN_Poffset button been pressed ?

   fRecallW       XW_fPressure                        ;Get current pressure sensor reading.

   fStore         fPoffset                            ;Store to Poffset.

* We are now waiting for the iBTN_Pcal button to be pressed before we calculate the span.

* Span is calculated as Pressure at 1m of water - fPoffset.

Calibrate_0:      ;0123456789012345

   OBLCD_SetCur   0,0

   OBLCD_Text     "Water level = 1m"

   OBLCD_SetCur   1,0                                 ;Display message.

   OBLCD_Text     "Press Pspan     "

   WaitOn         iBTN_Pspan                          ;Has iBTN_Pcal button been pressed ?

   fRecallQ       XW_fPressure                        ;Get current pressure sensor reading.

   fRecallW       fPoffset                            ;Calculate span.

   fSub

   fStore         fPspan                              ;Store to Pspan.

   SetMem         bCalibratedFlag,CalFlag             ;Set a flag to show at startup calibration has already been done.

   ShadowWrite    ShadowStart,ShadowLength            ;Save calibration parameters and flag.

   GoTo           MSQ_Start

 ****************************

 ** Xwire Error Check Task **

 ****************************

Xwire_Task:

XW0:

   YieldTask

   XwireGetErrCount                                   ;Get the xwire error count.

   GoIfF          XW1

   On             oXW_XWireError                      ;Turn on error LED if there are XWire errors.

   Pause          1

   GoTo           XW0

XW1:

   Off            oXW_XWireError

   GoTo           XW0

   NVEM0

XWireTab:         ;Slave address, transmit data block, Tx bytes, receive data block, Rx bytes.

   NV0Byte        XW_Address,XW_TxRAMstart,XW_TxRAMlength,XW_RxRAMstart,XW_RxRAMlength

   NV0Byte	   255					     ;End of table sentinel.


SX10507: pH Measurement

Previous Topic

Next Topic

In This Chapter

SX10507: General

SX10507: Specifications

SX10507: Connecting to your controller

SX10507: Usage Notes

pH measuring analysis

SX10507: pH Demo Software


SX10507: General

The SX10507 Xwire peripheral board interfaces raw pH probes (no in built preamplifier) to the SPLat Xwire expansion bus. The pH measuring input has a very high input impedance and is optically isolated, to ensure that the pH probe accuracy is not affected by ground currents. In addition it has inputs for a thermistor temperature sensor or a PT1000 RTD temperature sensor. These temperature sensing inputs can be used to temperature compensate the pH value and as well as general temperature measurement. They can be used at the same time.

An on board option link changes the gain of the input amplifier to allow ORP probes to also be used with the SX10507.


SX10507: Specifications

Isolated front-end | Yes |
pH input current | 4 (max) | pA
pH Resolution | 0.005 | pH
pH connection | On board BNC connector, plastic threaded for isolation |
Supply voltage range | 12 –24 | Volts DC
Supply current | 65/70 | mA (12/24 VDC)
RTD type | PT1000 |
RTD resolution | 0.13 | °C
RTD accuracy | 1 | %
Thermistor resistance | 2K – 10K, 10K preferred | Kohms
Thermistor resolution | 0.3 | °C (10K thermistor, 0 – 60°C)
Thermistor Excitation Voltage | 10 | Volts
Thermistor Source Resistance | 39K | KOhms
Xwire interface | TTL and RS485 | Pin Jumper selectable
Xwire address range | 0 – 249 | Pin jumper selectable
Xwire pH data | Floating point number normalised to 1.0 |
Xwire Thermistor data | Floating point number normalised to 1.0 |
Xwire RTD data | Floating point number normalised to 1.0 |


SX10507: Connecting to your controller

The board can connect to your controller in one of two ways: Via the 2 pin TTL Xwire connector or via an RS485 adaptor, if your controller does not have an RS485 Xwire interface. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol that is used to convey data across the interface and wiring. The 2 pin TTL Xwire electrical interface is good for a few feet and up to 10 boards. The final choice of interface is dependent on the distance and the electrical environment (ie: the amount of electrical noise).

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 will allow up to 32 boards to be connected. The programming is exactly the same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller. The RS485 interface is a lot more forgiving in terms of noise and is better protected against electrical transients and ground loop problems.

Selecting the Xwire or RS485 interface

A 3-pin jumper on the board is labelled 'RS485 X'. Inserting a jumper in the two pins labelled RS485 will activate the RS485 interface. Inserting a jumper in the two pins labelled X will activate the Xwire electrical interface.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled X1 and X2. These are the TTL Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system.

The G terminals on all boards in a system should all be connected together. For best results, especially over long distances and/or where the devices each end are powered off different power supplies, use shielded pair cable, and use the shield for the G connection.

Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line termination resistor. At the 38400 baud used by Xwire termination is not very critical. One termination resistor in a complete system is generally adequate.


SX10507: Usage Notes

It is beyond the scope of this document to advise the user about the care of pH probes. However, it is very important to understand that the pH input on the SX10507 has a very sensitive and high input impedance and therefore the SX10507 should be mounted as far away as possible from electrical noise sources. This goes also for the pH probe wiring. Always run this wire by the shortest route and away from other wiring. Do not handle the board unnecessarily, especially around the BNC connector. Residue on the PCB will affect the pH readings.

The front end of the SX10507 (i.e. the circuitry that connects to the actual probe) is electrically isolated from the rest of the board. This means there is no direct wire connection from your SPLat controller to the probe. This isolation serves to reduce the likelihood of electrical noise or hum from upsetting the readings. If you are simply measuring the pH of a solution in a beaker on the lab bench, this will not make any difference. If you are measuring the pH of something in a real-world vessel, swimming pool or anything else that is not insulated from its surroundings, this will make a huge difference.


pH measuring analysis

Introduction

pH probes provide a voltage that is a direct relation to the solution's pH and temperature:

  • pH probe's output is measured in mV.
  • the result is always mV = 0 for a pH = 7, for all temperatures.
  • The reading is linear in relation to the solution's acidity.
  • The slope of the reading is linear in relation to the temperature.
  • at 0 degC the slope is 54.20 mV/pH
  • at 25 degC the slope is 59.16 mV/pH
  • at 100 degC the slope is 74.04 mV/pH

Calculating pH

If X is the slope of the pH reading for a given temperature, then the pH is given by:

pH = 7.0 – V/X With V the measured voltage of the probe in mV.

And X is given by:

X = 0.1984 * T + 54.20 With T in degrees Celcius.

Measuring temperature

Temperature is measured using an RTD (Resistance Thermal Device). Its resistance is given by the following quadratic equation:

Rt = R0*( 1 + A*T + B*T*T)

With:

R0 = 1000

A = 0.0039083

B = -0.0000005775

Using a linear approximation for estimating the temperature using the thermal resistance.

Rt = 1000 * ( 1 + 0.0039083 * T ) Which gives;

T = ( Rt – 1000 ) / 3.9083

The max temperature approximation error is 1.5 degrees C.

Sensitivity of the result to the different parameters

The biggest variation of the pH in relation to the measured voltage will be where X is the smallest, i.e at 0 degrees Celcius:

A variation of the measured voltage by 1mV will change the pH value by at most 0.000018

The biggest variation of the pH in relation to the measured temperature will be where X is again the smallest, i.e between 0 and 1 degree Celcius, and at the highest measured voltages, i.e –500 mV:

A variation of the measured temperature by 1.5 degree Celcius will change the pH value at most by 0.06

Calibrating a pH probe and electronics

The electronics and probe will introduce errors in the form of drift of the reference and slopes. To correct this, calibration is needed and is done in the following manner:

On a regular basis, the system will be calibrated using solutions at known pH.

As the solutions are at a known pH, the expected measured valued are known, and therefor can be used in comparison with the actual measured values to infer a calibration correction.

Method:

The correction will be a linear function: transforming a measured voltage into an expected voltage.

V = a * v + b With V = expected voltage and v = measured voltage.

The two buffers used will have a pH of 7.0 and 4.0, the current temperature is important for calculating the expected voltage for the pH = 4 solution.

If vph4 and vph7 are the measured values at the current temperature tcur , then we have:

a * vph7 + b = 0

a * vph4 + b = 3.0 * ( 0.1984 * tcur + 54.20)

a = 3.0 * ( 0.1984 * tcur + 54.20) / (vph4 - vph7)

b = - a * vph7

Note: once a and b are calculated, then the expected voltage in the range –600 mV to 700 mV will be calculated using the formula: V = a*v + b, with v being the measured value, no matter what the electronic circuit or measuring range or number system is used.

Effectively calculating pH

It is assumed that the following data is known:

  • a calibration data
  • b calibration data
  • Rt measured resistance off temperature probe.
  • Vp measured voltage off pH probe.

1st step: calculate theoretical voltage of the probe.

V = a * Vp + b

2nd step: calculate temperature.

T = ( Rt – 1000 ) / 3.9083

3rd step: calculate temperature correction

X = 0.1984 * T + 54.20

4th step: calculate pH

pH = 7.0 – V/X


SX10507: pH Demo Software

pHDemo.spt is a full-fledged application program for the SX10507 Xwire pH interface board. It demonstrates the following in regards to the use for the SX10507 and the calibration of pH probes:

  • The Xwire configuration required to receive data from the SX10507
  • How to apply noise filtering to pH readings, RTD readings and Thermistor readings
  • How to calibrate a pH system
  • How to convert PT1000 RTD readings into °C or °F
  • How to convert thermistor readings into °C or °F
  • How to temperature compensate pH readings

The program is well documented and this document is a guide to the use of the program from a users point of view, rather than the software methods used.

Previous Topic

Next Topic

In This Chapter

SX10507 Demo: What You Will Need

SX10507 Demo: Option Links

SX10507 Demo: Connecting Up

SX10507 Demo: Software Operation

SX10507 Demo: Calibration Mode

SX10507 Demo: Calibration Summary


SX10507 Demo: What You Will Need

You will need the following to test and demonstrate the SX10507:

  • Mmi202aDK216 SPLat controller board with at least version 3.22 Firmware loaded
  • The latest pHDemo.spt source file
  • SX10507 Xwire pH peripheral expansion board
  • pH probe with a BNC connector as termination for the pH probe
  • TTL Xwire cable (2 wire for use over short distances)
  • DC power supply, 12 or 24 Volts, 200mA or greater
  • pH 7 buffer solution
  • pH 4 buffer solution
  • Distilled water
  • Optional - PT1000 RTD sensor, stand alone or built into the pH probe
  • Optional - 2K to 10K thermistor, stand alone or built into the pH probe **

** Note: If a thermistor is used, the demonstration program will require the linearising polynomial to be updated with the correct coefficients for the chosen thermistor. See: Tutorial: Thermistor temperature measurement


SX10507 Demo: Option Links

We start by ensuring the links on the SX10507 are correctly installed for the demonstration software.

The dual row of Xwire address jumpers next to the TTL Xwire connectors should all be removed. This will set the address of the SX10507 to be zero, which is what the software expects.

LK1 behind the BNC socket should be installed. This sets the amplifier gain for pH probe level signals rather than ORP probe signals.

The Xwire mode link marked "X RS485" should be in the X position to enable TTL Xwire communications. Using the RS485 option is not covered in this document.

The "Term" link next to the terminal block at the Xwire connector end can be left off.

This completes the set up of the option links.


SX10507 Demo: Connecting Up

Both the MMi202 controller and the SX10507 should be wired up to the DC power supply. Any DC power supply in the range of 12 – 24 VDC will do. Power on the SX10507 is connected via the terminal block on the "0V" and "+V" terminals (positive terminal).

Connect the two boards via the 2 pin Xwire cable.

The pH probe should be plugged into the BNC connector.

If you have a PT1000 RTD sensor, this can be attached to the terminal block terminals marked PT1000. Likewise, the thermistor if present can be connected to the "Thermistor" terminals. Note a temperature sensor (PT1000 or thermistor) is NOT required to run the demo program as the program allows the temperature to be manually entered on the fly to calibrate and temperature compensate the pH readings. If a thermistor is used, the program will have to be modified to suit its parameters.

The RS485 connection option is beyond the scope of this document.

Power the system up and connect the MMi202 to your PC. Down load the PHDEMO.spt file to the board and run it.


SX10507 Demo: Software Operation

Turn On

Whenever the program is down loaded to the board, the non volatile memory or permstore memory is erased, and this is where the calibration and setting are held. The program checks for valid contents of permstore memory on power up and will load some default values if the permstore memory has been erased. If this occurs, the display will read: "Default Values Loaded" and the operating parameters will be loaded with the following defaults:

  • Temperature display mode: Fahrenheit
  • Temperature Compensation source: USER
  • PH signal filtering: OFF

In addition, the calibration values for the pH probe will be loaded with some values that will at least give an approximate reading on the pH display.

There after, the display will start with the normal status screen:

"pH= 7.01 T= 77.0"

"Filt:0 USER "

This screen shows that the measured pH is 7.01 (in this case) the compensation temperature is 77°F, the pH filter is set to zero (filter is off) and the temperature compensation source is USER, which means that the temperature value is being supplied by the user via the front panel push buttons on the MMi202.

Front Panel Push Buttons

MMii99 2x16 overlay with i-o numbers for web

None of the front panel LEDs are used. Some of the front panel push buttons have a dual function depending on which mode the program is operating in. There are three modes:

  • Run Mode –pH and temperature status being displayed
  • Edit Mode – system options and parameters being edited
  • Calibration Mode – pH system being calibrated

Run Mode

In Run mode the buttons have the following functions:

  • Input 12 – increment USER temperature value
  • Input 11 - decrement USER temperature value
  • Input 10 - ** no function **
  • Input 9 - ** no function **
  • Input 8 – Menu / Calibration mode entry button

If the temperature compensation source has been set to USER (by editing the parameter in the menu mode), then Input 12 and Input 11 increment and decrement respectively the compensation temperature, and the LCD screen value will be updated in real time.

Input 8 sets the software into one of two modes depending on how long the button is pressed for. If it is pressed for less than 6 seconds (short press), the parameter-editing mode (menu mode) is entered. If the button is pressed for 6 seconds or longer (long press), the unit enters the calibration mode. Once calibration mode has been chosen, the only way to abort it is to remove power from the unit. Progressing through the calibration mode without following the calibration procedure and using the correct buffer solutions will render the pH readings useless, hence the 6 second push to enter this mode. Calibration can always be repeated to restore the unit to correct operation.

Menu Mode

In Menu mode, operating parameters can be changed:

  1. Temperature compensation source: USER, THERM (thermistor) or RTD. This selects the temperature source that will be used to calibrate the pH system when in calibration mode, and to temperature compensate the pH reading in Run mode.
  2. Displayed temperature units: Celsius or Fahrenheit
  3. pH filter setting: 0 (off) à 6 (heavy filtering). Allows the user to get a more stable reading in the presence of high electrical noise levels. With no noise sources present, the filter can be turned off (set to zero).

In Menu mode the front panel buttons have the following function:

  • Input 12 – Parameter increment button – increments/changes the currently displayed parameter value
  • Input 11 - Parameter decrement button – decrements/changes the currently displayed parameter value
  • Input 10 – ** no function **
  • Input 9 – Parameter select button – steps through parameters which can be changed
  • Input 8 – Exit and store new parameter values


SX10507 Demo: Calibration Mode

To calibrate the pH system, you must have fresh pH7 and pH4 buffer solutions. The temperature of these solutions must also be known at the time of calibration. If your pH probe has a PT1000 RTD sensor, then set the temperature compensation source to RTD and the temperature compensation will be done automatically. Similarly, if the program has been set up for a thermistor (correct polynomial values entered into the source code), then again the temperature compensation will be automatic.

If the pH probe does not have a temperature sensor, then the pH buffer solution temperature can be entered via the front panel buttons. For this, you will need to know the temperature of the buffer solutions by measuring it manually with a thermometer.

A table on the side of the buffer solution bottles shows what the actual pH is at the measured temperature and this must be entered via the front panel push buttons during calibration.

In calibration mode, the front panel push buttons have the following functions:

  • Input 12 – Increment the USER temperature setting (temperature of buffer solution)
  • Input 11 - Decrement the USER temperature setting (temperature of buffer solution)
  • Input 10 – Increment by 0.01 pH of buffer solution at the measured temperature (from table on buffer solution bottle or documentation)
  • Input 9 – Decrement by 0.01 the pH of buffer solution at the measured temperature (from table on buffer solution bottle or documentation)
  • Input 8 – Calibration Step Button – Stages calibration procedure using LCD messages.

pH 7

After a long press of Input 8 (Menu / Calibration mode button), the unit will beep and display will display the following screens:

"pH Calibration "

" Mode "

"Use pH7 buffer "

"Set Temp and pH"

The pH probe must be place in pH buffer 7 solution. The LCD screen will look like this:

"Reading: 0.4986 "

"pH= 7.00 T= 77.0"

The reading is the raw value from the SX10507 and is displayed so that you can tell when the reading has settled (ie: value is no longer changing).

The temperature value T is displayed from the currently selected temperature source (as set previously in the Menu mode). If it is from the RTD or thermistor and the temperature sensor is in the buffer solution, nothing has to be done about it. If the temperature source is USER, then buttons Input 12 and Input 11 allow the user to set the temperature of the buffer solutions as they are used.

The pH value is the value that must be set for the measured pH 7 buffer solution temperature, using the buttons Input 10 and input 9. For example, if the temperature of the pH 7 buffer solution is 68°F (20°C), then from the pH table supplied with the buffer solution, the actual pH at this temperature of the solution is 7.02. In this case the user would press Input 10 button twice to get the displayed value to be 7.02.

Once the Reading is stable and the temperature and calibration pH values are correct, press Input 8 button – Calibration step button.

pH4

The display will now read:

"Reading: 0.5524 "

"pH= 4.00 T= 77.0"

Again, the temperature must be entered if the temperature source is USER.

The pH value must be adjusted using the tabulated data that came with the buffer solution.

Once this has been done and the Reading is stable, press Input 8 button, calibration step button.

Congratulations, you have now calibrated your pH system!

Previous Topic

Next Topic


SX10507 Demo: Calibration Summary

Before you start:

  1. Select via the menu mode, the temperature source you intend to use for calibration
  2. Select via Menu mode, a suitable filter setting to get a stable pH reading in the status screen
  3. Select via the Menu mode, the temperature units you wish to work in (Fahrenheit or Celsius)

Calibrating:

  1. Enter calibration mode with long press, Input 8
  2. pH Probe in PH7 buffer solution, adjust USER temperature (if temperature source is set to USER, Input 12 and Input 11)
  3. Enter the tabulated buffer pH value for the buffer solution temperature (Input 10 and Input 9)
  4. Wait for the Reading to be stable
  5. Press Input 8 button to advance to the pH4 calibration
  6. pH Probe in PH4 buffer solution, adjust USER temperature (if temperature source is set to USER, Input 12 and Input 11)
  7. Enter the tabulated buffer pH value for the buffer solution temperature (Input 10 and Input 9)
  8. Wait for the Reading to be stable
  9. Press Input 8 button to finish calibration.

Notes:

  • Rinse probe in distilled water between dipping it into pH buffers to avoid contamination
  • Shake off distilled water from probe to avoid diluting the pH buffer solutions
  • Always store the probe in the solution supplied with the probe (KCl solution), NOT distilled water.


SX10508: Xwire SD Logger

  • Xwire and RS485 interfaces
  • SD and SDHC compatible
  • FAT12, FAT16 and FAT32 support
  • 4GB maximum log file size
  • Battery backed clock

This board allows you to log data from your SPLat controller onto a normal SD memory card, the same as those used in most digital cameras.

The SX10508 gives you a choice of using the Xwire cabling for short cable runs between boards, or RS485 for long runs. Xwire is suitable for "inside the same box" use (a few feet) and has virtually zero hardware cost. RS485 is suitable for tens or hundreds of feet (depending on a number of factors) but requires an RS485 adaptor on the controller (whereas the SX10508 has RS485 built in, our standard controllers do not).

Previous Topic

Next Topic

In This Chapter

SX10508: Connecting to your controller

SX10508: Power supply requirements

SX10508: Setting the time and date

SX10508: Inserting and removing the SD card

SX10508: Programming

SX10508: Example Program


SX10508: Connecting to your controller

The board can connect to your controller in one of two ways: Via Xwire or via RS485. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol (language) that is used to convey data across the interface and wiring. The Xwire electrical interface is good for a few feet and up to 10 boards.

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 does not specify a protocol. When you use RS485 with this board it will use the Xwire protocol with up to 32 boards. The programming is the exact same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller.

Selecting the Xwire or RS485 interface

A 3-pin jumper on the board is labelled 'RS485 Xwire'. Inserting a jumper in the two pins labelled RS485 will activate the RS485 interface. Inserting a jumper in the two pins labelled Xwire will activate the Xwire electrical interface.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled Xwire. These are the Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy-chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to theright above) is a jumper marked "485Term". Installing a jumper in that position provides an RS485 line terminator.

Xwire operates at 38.4KB, which is slow for RS485, hence it is very reliable. The connection is "two wire, half duplex".


SX10508: Power supply requirements

The board will run off 10VDC at 50mA to 24VDC at 25mA. The voltage is allowed to go to a maximum of 32VDC.

Connect power to the 3-pin connector depicted above. The positive terminal is in the middle. There are 2 ground pins simply because we used a 3-pin connector to make it unique.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals of all power supplies and boards must all be connected together or smoke will come out.

Previous Topic

Next Topic

In This Section

Some common terminology


SX10508: Setting the time and date

Setting the time and date is optional but useful if you would like to take logs at timed intervals and stamp each entry in the log file with the time of the log. To set the time and date you will need to connect your PC to the TTL Comms header using a SPLat PC232 cable and use a terminal program to interact with the SD Logger board.

A suitable terminal program is Realterm which is free to use.

Configure terminal program to communicate with the SX10508:

The baud rate should be set to 38.4kbps, no parity, 1 or 2 stop bits, no flow control.

Once the terminal program is configured, press enter and you will see the Choose Option screen:

Choose Option screen:

 ================================
 = S P L a t    C o n t r o l s =
 =         Xwire Logger         =
 =    http://www.splatco.com    =
 ================================
 Please choose an option:
  1) Set the time.
  2) Set the date.
 >

Press 1 to go to the Set Time screen.

Press 2 to go to the Set Date screen.

Set Time screen:

 ================================
 =          Set Time            =
 ================================
 Enter the time in 24hr format,
 for example, 6:35 or 17:05.
            Press Enter to set or
                Escape to cancel.
 >

Enter the time in 24 hour format and press enter. The time is now set.

Set Date screen:

 ================================
 =          Set Date            =
 ================================
 Enter the date as YYYY/MM/DD,
 for example, 2010/2/25.
            Press Enter to set or
                Escape to cancel.
 >

Enter the year then month then day and press enter. The date is now set.


SX10508: Inserting and removing the SD card

Insert the card as shown in the picture below. The card will not go into the socket if it's around the wrong way, so don't use force when you insert it.

When the card is correctly inserted, the LED will turn red to indicate the card is now in use and should not be removed. If there is a problem with the card, the LED will flash red. In this case, make sure the card is not formatted with NTFS or file system other than FAT. Also make sure there is space on the card and it is not write protected.

To remove the card, first press the button next to it.

Wait for the LED to turn green then pull the card gently out of the socket.

If power is lost OR if the eject sequence is not followed correctly there is no guarantee that data will be saved.


SX10508: Programming

The SX10508 uses 3 XWire addresses.

Xwire address 10 handles the Data that is sent to the SX10508 to be logged.
This consists of an Xwire transmit and receive data block.

Xwire address 11 handles the Time from the SX10508 real time clock.
This is optional and consists of receive data block.

Xwire address 12 handles the Date from the SX10508 real time clock.
This is optional and consists of receive data block.

The following tables depict how the Xwire data blocks need to appear in memory.

Xwire address 10: Logging Data

Xwire transmit data block:

Memory | Description | Format
0 | Tx Sequence number | Byte
1 | Length | Byte
2 | Data0 | Byte
3 | Data1 | Byte
... | ... | ...
n | DataN | Byte

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory location 0 in the Xwire transmit data block corresponds to the 'Tx Sequence number'. The Tx sequence number consists of a single byte in the range of 0 – 255.

When the 'Length' and 'DataX' bytes have all been set, change the 'Tx Sequence number' byte to instruct the logger to commit the data to the log file.

Memory location 1 in the Xwire transmit data block corresponds to the 'Length' byte. This byte is set to the number of bytes of data that the Data Logger needs to log.

Memory location 2, 3, …, n in the Xwire transmit data block is where log data is stored to be transferred to the Data Logger for logging. The maximum number of bytes that can be reserved is 30 bytes.

The Transmit data block may consist of up to 32 bytes total.

Xwire receive data block:

Memory | Description | Format
0 | Rx Sequence number | Byte
1 | Result | Byte

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory location 0 in the Xwire receive data block corresponds to the 'Rx Sequence number' byte from the Data Logger. The 'Rx Sequence number' byte will be set to the same value

that is currently present in the 'Tx Sequence number' when the logger has successfully committed the data to the log file.

Memory location 1 in the Xwire receive data block corresponds to the 'Result' byte from the Data Logger. After a log attempt, the 'Result' byte will be set to a value of 0-5 depending on whether it was a success or a failure.

The following table depicts the result codes:

Result byte | Description
0 | Successful
1 | Card Not Present
2 | Card Write Protected
3 | Unknown File system
4 | Open Error
5 | General Failure

Xwire address 11: Time

Xwire receive data block:

Memory | Description | Format
0 | Hours | Byte
1 | Minutes | Byte
2 | Seconds | Byte

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory location 0 in the Xwire receive data block corresponds to the 'Hours', this is where the hour count from the Data loggers Real Time Clock will appear.

The 'Hours' byte is in 24-hour format.

Memory location 1 in the Xwire receive data block corresponds to the 'Minutes', this is where the minute count from the Data loggers Real Time Clock will appear.

Memory location 2 in the Xwire receive data block corresponds to the 'Seconds', this is where the second count from the Data loggers Real Time Clock will appear.

Xwire address 12: Date

Xwire receive data block:

Memory | Description | Format
0 | Day of the Week | Byte
1 | Day of the Month | Byte
2 | Month | Byte
3 | Year | Byte

*Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory location 0 in the Xwire receive data block corresponds to the 'Day of the Week', this is where the day of the week from the Data Loggers Real Time Clock will appear.

The byte will contain a number 0-6. 0 = Sunday. 6 = Saturday.

Memory location 1 in the Xwire receive data block corresponds to the 'Day of the Month', this is where the day of the month from the Data Loggers Real Time Clock will appear.

Memory location 2 in the Xwire receive data block corresponds to the 'Month', this is where the month from the Data Loggers Real Time Clock will appear.

Memory location 3 in the Xwire receive data block corresponds to the 'Year', this is where year from the Data Loggers Real Time Clock will appear. This is the year since 2000, so if a number of 10 was recalled from the 'Year' byte it would mean it's 2010.


SX10508: Example Program

Example program demonstrating how to interface with a SX10508

  • Program written for a MMI202a.
  • The MMI202a communicates to the SX10508 over the XWire peripheral interface.
  • MMI202a controller is the XWire master and the SX10508 is the XWire slave.

;When the user presses the MMI202a front panel push button iLOG_LogData, the current time

;from the SX10508 Real Time Clock is moved to the Xwire Transmit data block starting at

;bTx_LogData0 and is stored in ASCII format. This forms the time stamp for the data to follow.

;

;The data to log is now moved to the Xwire transmit data block and stored in ASCII format.

;This will be the logged data which will appear after the time stamp.

;

;Because all data that was stored to the Xwire transmit data block incremented the index register

;the value of I is stored to bTx_LogLength so the SX10508 knows how many bytes of data it

;needs to log.

;

;Data is stored in ASCII format so it can be read with a text editor on a PC.

 * Equates *

 * XWire TIME Equates *

XW_TimAddress:       EQU   11       ;Xwire Address of slave

XW_TimTxRamLength:   EQU   0        ;number of bytes the XWire Tx data block consists of.

XW_TimRxRamLength:   EQU   3        ;number of bytes the XWire Rx data block consists of.

XW_TimTxRamStart:    EQU   0        ;ram length of 0 so set ramstart to 0.

 * XWire TIME Receive data block *

 * Data block of 3 bytes *

bRx_TimHour:         mEQU  20       ;Hour from the SX10508 Real Time Clock.

bRx_TimMin:          mEQU  21       ;Minute from the SX10508 Real Time Clock.

bRx_TimSec:          mEQU  22       ;Second from the SX10508 Real Time Clock.

 * XWire LOG Equates *

XW_LogAddress:       EQU   10       ;Xwire Address of slave

XW_LogTxRamLength:   EQU   17       ;number of bytes the XWire Tx data block consists of.

XW_LogRxRamLength:   EQU   2        ;number of bytes the XWire Rx data block consists of.

 * XWire LOG Transmit data block *

 * Data block of 17 bytes *

bTx_LogSeqNumber:    mEQU  25       ;Sequence number which instructs the SX10508 to commit

                                    ;the data to the log file when it is incremented.

bTx_LogLength:       mEQU  26       ;Number of Data bytes to be logged is stored in this byte.

bTx_LogData0:        mEQU  27,15    ;First Logging Data byte in the XWire transmit data block.

                                    ;Reserve 15 bytes. 9 bytes of ASCII data for time stamp, 6 bytes

                                    ;for Logged ASCII data and carriage return/line feed characters.

 * XWire LOG Receive data block *

 * Data block of 2 bytes *

bRx_LogSeqNumber:    mequ  23       ;when 'bRx_SequenceNumber' becomes the same as

                                    ; 'bTx_SequenceNumber' the write operation has been completed and successful.

bRx_LogResult:       mequ  24       ;Contains the result code after the write operation is completed.

 * Input Equates *

iLOG_LogData:       iEQU   8        ;front panel Pushbutton to log the current data.

   XwireMaster    XwireTab             ;start xwire going

LOG_0:

   GoIfInK        iLOG_LogData,LOG_1   ;Force data logging if user presses iLOG_LogData.

   GoTo           LOG_0

LOG_1:

* Print time stamp *

   LoadI          0                    ;Initialise the index register.

   Recall         bRx_TimHour          ;get the current hour

   iiuPrintXVW    bTx_LogData0         ;store Time in ascii format in the XWire Transmit block for

                                       ;SX10508 to access. I is incremented by the number of characters stored.

   LoadX          58

   iiChrPrintX    bTx_LogData0         ;print : character for the time stamp.

   Recall         bRx_TimMin           ;get the current Minute

   iiuPrintXVW    bTx_LogData0         ;store Time in ascii format in the XWire Transmit block for

                                       ;SX10508 to access. I is incremented by the number of characters stored.

   LoadX          58

   iiChrPrintX    bTx_LogData0         ;print : character for the time stamp.

   Recall         bRx_TimSec           ;get the current second

   iiuPrintXVW    bTx_LogData0         ;store Time in ascii format in the XWire Transmit buffer for

                                       ;SX10508 to access. I is incremented by the number of characters stored.

   LoadX          32

   iiChrPrintX    bTx_LogData0         ;print space character to separate time stamp from logged data.

 * Print Data *

   LoadX          68                   ;D

   iiChrPrintX    bTx_LogData0

   LoadX          97                   ;a

   iiChrPrintX    bTx_LogData0

   LoadX          116                  ;t

   iiChrPrintX    bTx_LogData0

   LoadX          97                   ;a

   iiChrPrintX    bTx_LogData0

   LoadX          13                   ;ASCII 13 = carriage return

   iiChrPrintX    bTx_LogData0

   LoadX          10                   ;ASCII 10 = line feed

   iiChrPrintX    bTx_LogData0         ;Print Carriage Return Line Feed to signal end of line.

   ItoX                                ;number in I corresponds to the number of bytes that were

                                       ;printed to the XWire Transmit buffer.

   Store          bTx_LogLength        ;store this number for the SX10508 to access so it knows

                                       ;how many bytes of data it needs to log.

   IncM           bTx_LogSeqNumber     ;when 'bTx_LogSeqNumber' is incremented the SX10508 will

                                       ;log the current data.

   GoTo           LOG_0

   NVEM0                               ;End of program code, start of NVEM0 data tables

XWireTab:

   NV0Byte XW_LogAddress,bTx_LogSeqNumber,XW_LogTxRamLength,bRx_LogSeqNumber,XW_LogRxRamLength ;LOG

   NV0Byte XW_TimAddress,XW_TimTxRamStart,XW_TimTxRamLength,bRx_TimHour,XW_TimRxRamLength      ;TIME

   NV0Byte 255                 ;End of table sentinel.


SX10509: Ethernet/Internet "SPLat Call Home"

The SX10509 SPLat Call Home board provides your SPLat controllers with a versatile, easy to program means of communicating via the Internet. It is suitable for Machine To Machine (m2m) applications as well as remote monitoring and control involving interaction with end-users. SPLat Call Home technology minimizes Internet access problems caused by network security measures, while sophisticated data handling is easily provided by an Internet server.

Once the data from the SPLat is in the server (tutorial showing how simple this can be, with a fully worked example to get you started), the possibilities are limited only by server programming. This includes

  • Remotely viewing controller operating data/status via a web browser;
  • Sending commands back to the SPLat controller, for example to change a setting or enable an operation;
  • Generating email notifications, for example weekly performance summaries;
  • Generating text messages via an SMS gateway, for example alarms;
  • Logging long term data, for example production stats, energy savings in a solar installation;
  • Integration into other web-based enterprise systems, for example asset management.

Previous Topic

Next Topic

In This Chapter

SX10509: General principles

SX10509: Connecting to your controller

SX10509: Power supply requirements

SX10509: Connecting SCH to Ethernet/LAN

SX10509: Configuring your SCH

SX10509: Programming the SPLat controller


SX10509: General principles

The SX10509 connects to a SPLat controller via Xwire on one side and to the Internet, via Ethernet ("blue cable"), on the other side.

The SX10509 has been designed to communicate with an Internet server. The server can be yours, if you have such facilities, ours, if you want us to provide a service, or part of a regular web site hosting package with a 3rd party web host. Typically the SX10509 sends information to the server, where it is saved in an SQL database. A remote user can then access it (typically from a web browser). The SX10509 also extracts information (say commands and settings) from the server. In this way the server acts as an intermediate "drop box" between the SX10509 and the remote user. The server also serves as a powerful platform for advanced application programming.

All data exchanges are initiated by the SPLat controller via the SX10509. The SX10509 will look just like a regular web browser. It will therefore (more often than not) get out through the local firewalls to the Internet, without the need for any special configuration of the LAN it is connected to. This "little trick" is why we call it SPLat Call Home. The SPLat always "calls home" to send (upload) data and ask for (download) instructions. The alternative would be to have a web server in the SX10509. That would however mean any remote contact requests would have to get in through a firewall. This could require significant network administration skills. It would also make the programming much more complicated.

You do require programming on the server side. This is typically done as a php script. php programming skills are quite common - most capable web site programmers know php, and the related data base programming. Also, there exists a vast body of free tools and paid services for data management and presentation (graphs, tables, spread sheet files, sending emails etc).

On the SPLat side you program the SPLat controller to manage the SX10509, using regular SPLat instructions.

The SX10509 has separate Xwire addresses for:

  • Data to and from the server and commands to and from the SX10509 itself. This is called the Network Address. Commands include writing and reading data, setting the server and script file names and various other housekeeping functions.
  • Reading the time (derived from an Internet time server)
  • Reading the date (derived from an Internet time server)

The time and date addresses are very easy to read out, using the simplest Xwire technique that automatically copies the data to RAM in the SPLat. The Network address uses a simple technique for sending a range of commands and data to the SX10509 and reading data back from it.


SX10509: Connecting to your controller

The board can connect to your controller in one of two ways: Via the 2 pin TTL Xwire connector or via an RS485 adaptor, if your controller does not have an RS485 Xwire interface. You have to decide which interface you are using, select it via a jumper and then connect to the corresponding external terminals.

Xwire is our proprietary serial peripheral bus. It consists of the Xwire electrical interface and the Xwire data protocol that is used to convey data across the interface and wiring. The 2 pin TTL Xwire electrical interface is good for a few feet and up to 10 boards. The final choice of interface is dependent on the distance and the electrical environment (ie: the amount of electrical noise).

RS485 is an industry standard electrical interface suitable for direct wire connection between devices located up to hundreds of feet apart. RS485 will allow up to 32 boards to be connected. The programming is exactly the same but you will be able to go further for the cost of an extra RS485/Xwire converter on the controller. The RS485 interface is a lot more forgiving in terms of noise and is better protected against electrical transients and ground loop problems.

Selecting the Xwire or RS485 interface

This is set via the Xwire Configuration tab.

Connecting to the Xwire interface

Two connectors on one long side of the board are labelled X1 and X2. These are the TTL Xwire electrical connections. The two connectors are wired in parallel on the board to facilitate daisy chaining, and are completely interchangeable. The left-hand pin of each connector is ground (0V). If you are wiring to other boards with X1/X2 connectors, simply wire like pins together. If wiring to MMi202 or SL100, see the Xwire connection instructions in their documentation. See also wiring in the Xwire documentation.

Connecting to the RS485 interface

The 3-pin screw terminal block is for RS485 connection. The A and B terminals should go to A and B on all other devices on the RS485 system. The G terminal should go to system ground. Just next to the terminals (to the left above) is a jumper marked "Term". Installing a jumper in that position provides an RS485 line terminator.


SX10509: Power supply requirements

The board will run off 12VDC to 24VDC. Maximum current draw (TTL interface, RS485 termination link open) is 30mA. Using RS485 and 75 Ohms of termination, the maximum current draw is 90mA. The voltage is allowed to go to a maximum of 32V.

Connect power to the 3-pin connector depicted above. The positive terminal is in the middle. There are 2 ground pins simply because we used a 3-pin connector to make it unique.

You can use the same power supply as for the controller and other boards in your system, or a separate one. However, the ground (0V) terminals of all power supplies and boards must all be connected together or smoke will come out.


SX10509: Connecting SCH to Ethernet/LAN

Before you can configure your SX10509 you will first need to connect it to your Local Area Network or LAN.

This is done simply by connecting a straight through patch lead from a router or switch that has access to your LAN, into the RJ45 connector on the SX10509.

When you are connected to the LAN power on your SX10509, you should see a green light lit on the RJ45 connector where you just plugged in the network patch lead. A green light means the SX10509 is connected to the LAN.

You are now ready to configure the SX10509.


SX10509: Configuring your SCH

To configure the SX10509 you connect it to your Local Area Network (LAN) and use a web browser (such as FireFox or Internet Explorer) on a PC connected to the same LAN to communicate with the SX10509.

The SX10509 will automatically configure itself on most networks when it is turned on. Various settings may be altered with a web browser on your PC provided the PC is connected to the same LAN as the SX10509. You cannot do this via the internet unless you have something fancy like a VPN connection or place the SX10509 in the DMZ.

To access the controller from your browser, first find the SX10509 ID number that is stuck to the board. Enter this ID number as a URL in your browser with "id" as the prefix. For example, if the ID is 3691053123, you would enter:

http://id3691053123

This should give you a screen in you browser that looks like this:

This screen comes from the webserver built into the SX10509.

Alternatively you can enter the SX10509 name into your browser if you know it. By default the controller name is SPLat. For example, if the controller name is SPLat, you would enter:

http://splat

The controller name may be changed via the Network Configuration tab.

You will now see the Status tab is highlighted and see realtime status information, such as date, time, CPU temperature (the processor on the SX10509) and Xwire status.

If you happen to be using a Unix/Linux computer, or an Apple product, they become http://id3691053123.local and http://splat.local.

If you are unable to contact the SX10509 via your web browser you may have encountered a security block. If that is the case, please see your local System Administrator.

Previous Topic

Next Topic

In This Section

SX10509: Status

SX10509: Xwire Configuration

SX10509: Server Configuration

SX10509: Network Configuration

SX10509: Time Configuration

SX10509: OTA Configuration


SX10509: Status

The Status tab allows you to see realtime status information, such as date, time, CPU temperature and Xwire status from the SX10509.

General

Local Time:

The current local date and time from the SX10509.

CPU Temperature:

The current temperature of the SX10509. This is measured from within the CPU and is displayed as degrees celsius.

Xwire

Port:

The current status of the Xwire port is displayed here.

If there is no Xwire traffic between the SX10509 and the SPLat controller or if the Xwire cable is unplugged "Disconnected" will be displayed.

If there is Xwire traffic between the SX10509 and the SPLat controller "Traffic present" will be displayed.

Xwire traffic that is not addressed to the SX10509 but another Xwire device on the Xwire bus will still display a "Traffic present" status as the SX10509 can see this as valid Xwire data.

Network:

If there is no Xwire communications between the SPLat controller and the "Network" Xwire address "No Poll" will be displayed.

If there is Xwire communications between the SPLat controller and the "Network" Xwire address "Running" will be displayed. Xwire Data To and From the SPLat controller will be displayed if there is Xwire communications to this Xwire address.

Time

If there is no Xwire communications between the SPLat controller and the "Time" Xwire address "No Poll" will be displayed.

If there is Xwire communications between the SPLat controller and the "Time" Xwire address "Running" will be displayed. Xwire Data To and From the SPLat controller will be displayed if there is Xwire communications to this Xwire address.

Date

If there is no Xwire communications between the SPLat controller and the "Date" Xwire address "No Poll" will be displayed.

If there is Xwire communications between the SPLat controller and the "Date" Xwire address "Running" will be displayed. Xwire Data To and From the SPLat controller will be displayed if there is Xwire communications to this Xwire address.


SX10509: Xwire Configuration

This tab allows you to configure Xwire communications between the SX10509 and its SPLat master.

Any changes you enter here will take effect once the change button is pressed.

Connection:

This is where you must select how the SX10509 is connected to its SPLat master. Selecting the option labelled RS485 will activate the RS485 interface. Selecting the option TTL will activate the Xwire electrical interface.

Network Address:

The unique Xwire address containing data that is sent to and from a server. This is set to 64 by default.

Controller Network Rx Size:

Size of the SPLat controllers Rx buffer for the above network address. May be between 3 and 32 bytes long. By default, this is set to 32 bytes.

Time Address:

The unique Xwire address that provides Internet synchronised Local time. This is set to 65 by default.

Date Address:

The unique Xwire address that provides Internet synchronised Local Date. This is set to 66 by default.


SX10509: Server Configuration

This tab allows you to configure the SX10509 to the default server you want it to talk to. If the SPLat controller sends commands to the SX10509 without defining a server, port, path and content type, the default server settings will be used. This can lead to a simpler program in the SPLat controller if you only require to talk to a single server.

Any changes you enter here will take effect once the change button is pressed.

Current

The "Current" area shows the values that will be used and this always reverts to the default values at power on.

URL:

Shows the current server, current port and current path.

Content Type:

Shows the current type of content to be transferred to the server.

Defaults

Values entered in the "Default" area are the power-up defaults. They will be used unless the controller sends different ones.

Server:

Set the server where the request will be sent.

Don't use a leading protocol, ie "http://" as this will automatically be appended by the SX10509. The server must not exceed 63 characters in length.

Port:

Set the server port where the request should be sent. The port is a SPLat floating point value and must not exceed 65535. The port is typically 80 when sending a request to a HTTP server.

Path:

Set the path to the script on the server. The path must not exceed 63 characters in length.

Content Type:

Set the content type when using a POST request. The content type must not exceed 39 characters in length.


SX10509: Network Configuration

This tab allows you to configure how the SX10509 communicates to the network it is connected to.

Any changes you enter here will take effect once the change button is pressed.

MAC Address:

Shows the unique MAC address of the SX10509.

IP Address:

Shows the IP address that has been assigned to the SX10509 for communications over the network.

Gateway Address:

The Gateway address that connects the internal network the SX10509 is currently connected to to the internet. Most probably the LAN address of your modem.

Subnet Mask:

The subnet mask for your LAN.

DNS Server:

The address of the DNS server as provided by your modem for the subnet mask.

Controller Name:

The name you give to the board to make it easier to remember and connect to with your browser. By default this is SPLat but you can change it if you have multiple SX10509s on the network. 15 alpha numeric characters maximum no spaces.

Proxy Server:

This is only applicable if your network uses a proxy server to connect to the internet.

Proxy Port:

Port of the Proxy server.

Proxy Username:

Authorised username of the Proxy server. If you use a proxy server that doesn't have a username/password leave this field blank.

Note: We only support Basic authentication and do not support Digest authentication.

Proxy Password:

Authorised password of the Proxy server corresponding tot he username. If you use a proxy server that doesn't have a username/password leave this field blank.


SX10509: Time Configuration

This tab allows you to configure the time.

Any changes you enter here will take effect once the change button is pressed.

NTP Server:

Set the server the SX10509 will synchronise its time and date with.

Timezone Offset, minutes:

Adjust the SX10509's clock to be accurate to your current timezone.

Local Time:

Current local time from the SX10509.


SX10509: OTA Configuration

This tab allows you to change how the SX10509 updates via the internet.

Any changes you enter here will take effect once the change button is pressed.

This tab is for diagnostics. If you are having trouble with your board SPLat may ask you to take note of this information.

Click to do an OTA Update:

This will force the SX10509 to check for any new software and update if a newer version is found.

Product:

The type of SPLat product to update.

Software:

The version of software currently residing in the connected SX10509.

Hardware:

The board revision of the connected SX10509.

Replaces Version:

The version of software preceding the current software in the board.

Module ID:

The unique board ID of this SX10509.


SX10509: Programming the SPLat controller

Once you have configured the SX10509 SPLat Call Home board for the Internet server and script page it is to communicate with, you can program the SPLat controller.

The SX10509 responds to 3 Xwire addresses, called Network, Date and Time. Most of the action takes place on the network address.

On the network address there are a range of commands that can be sent to the SX10509 by the SPLat controller. These include commands for writing data to the SX10509, sending (that) data to the server and reading back server responses to the SX10509. Hence, the SPLat controller is able to send whatever it wishes to the server. Data is sent to the server only when the SPLat program instigates it. Typically data exchanges with the server will be in ASCII (not binary) format.

The date and time addresses are used to simply read the current date and time out of the SX10509 for use in your program. The SX10509 gets this information from an Internet time server automatically.

There is an in-depth tutorial on programming for the SX10509, with a fully worked example, here.

Previous Topic

Next Topic

In This Section

SX10509: Server

SX10509: Date and Time

SX10509: Example Programs

SX10509: Tutorial and online sandbox


SX10509: Server

This section provides details of how you exchange data and commands between a SPLat controller and a server, via the SX10509. This page provides an overview of the process, while subsequent pages provide more specific details.

The SX10509 SPLat Call Home module is able to perform either GET or POST requests under control of the SPLat program in your controller.

The simplest method of configuring the server URL is to enter the server domain, port and script path in the Server Configuration tab.

Data is sent to and from a server, via the SX10509, at the Xwire address which is set via "Network Address" in the Xwire Configuration tab. This is 64 by default.

Both the Xwire network transmit and receive data blocks may be between 3 and 32 bytes long. Use the longest you can afford for each, based on the amount of spare RAM you have.

The length of your transmit data block is automatically detected by the SX10509, but the length of your receive buffer must be entered on the Xwire Configuration tab. By default, this is set to 32 bytes.

To get some data from the SPLat controller to the server, you first have to send it to the SX10509 board, and then command the SX10509 to send it to the server (usually with a GET or POST request). To get some data from the server you have to command the SX10509 to request it from the server (usually with a GET or POST request), and then read the resulting data out of the SX10509.

Transferring data from the SPLat controller to the SX10509

The basic steps for sending something to the SX10509 board via Xwire are:

  1. Load a text string of length 0 to 30 bytes (or less if you have allocated less than 32 bytes to the Xwire network transmit data block) into the Xwire network transmit data block, starting at byte 2. This is the data.
  2. Set byte 1 (the 2nd byte) of the block to the number of bytes in that string.
  3. Set byte 0 of the block (the TxCommand byte) to the command that tells the SX10509 what to do with the string. It is important that the command byte is set last.
  4. Wait for the command byte to be echoed in the first byte of the Xwire network receive data block (the RAM area that is designated for data from the SX10509's network address)

The SX10509 will process a command only when the command byte in byte 0 of the network transmit block is different to the last/previously processed command. You force the SX10509 to process several commands of the same type by toggling bit 7 of the command byte. The SPLat program should always wait for the echo (the identical command byte) to appear in byte 0 of the network receive block before moving on to a subsequent command. If the command involves sending something to the server, which could be the other side of the world, this could take some time.

The SX10509 contains a 256 byte RAM buffer for holding data that is to be sent to the server. Because the Xwire link between the SPLat controller and the SX10509 can only transfer a maximum of 30 bytes at a time, you may need to load up the server transmit buffer in several Xwire transfers. Each Write command appends its data to the buffer, except a Write of zero bytes, which clears the buffer.

Transferring data from the SPLat controller to the server (via the SX10509)

The basic steps for sending some data to the server are:

  1. Clear the server transmit buffer by writing an empty (zero length) string to it, using the steps above with a Write command
  2. For each item of data you want to send to the server print it to the Xwire network transmit block, using (typically) iiPrintText and iifPrintWFW instructions, along with appropriate field delimiters, and then send it to the SX10509's server transmit buffer with a Write command.
  3. Once you have transferred all you want to send to the server (up to the 256 byte limit), send the SX10509 a command to send the data to the server. This can be either via a GET or a POST (two of the ways a web browser sends a user's data to a server).
  4. Wait for the DoGET or DoPOST command to be echoed in byte 0 of the network receive data block in the SPLat.
  5. The second byte (byte 1) will now contain the count of how many bytes of data the server responded with (which are saved inside the SX10509 for later Read). Bytes 2-5 will contain the server's response code as a floating point number. These are the codes you sometimes see on your browser, like 404 - Page Not Found.
  6. Once you have a response code, and it is OK (no errors) you can extract any data/commands that have been returned by the server, using the byte count in byte 1 of the GET/POST command echo and Read command(s).

Transferring data from the SX10509 to the SPLat controller

The basic steps for extracting server data back from the SX10509 to the controller are:

  1. Do a GET or POST. This is what triggers the server to send some data. The server cannot initiate a transfer.
  2. Get the byte count from byte 1 (2nd byte) of the GET/POST command echo (above).
  3. Extract and process the data. The Read command to the SX10509 contains a parameter that specifies a cumulative offset into the SX10509's server receive buffer (where data from the server is held). The cumulative offsets let you access different parts of the buffer. Each time you send a Read command, the SX10509 will write the requested data to the Xwire network receive block and echo the Read command. The details of how you utilise the data in the receive block will depend on how the data is formatted by the server. You will generally need instructions like iifGetNum, which converts an ASCII numeric string such as "3.1415" to a floating point number in W, and iiGetHex.

Hint: When designing the server script, it is much safer and easier to use ASCII number representation than pure binary numbers.

The identity of the server that the SX10509 will communicate with is stored in the SX10509. This information can be configured into the SX10509 during system setup. It can also be changed dynamically via Network commands. Once you understand how to send and receive data, changing the server configuration programmatically will be simple.

Previous Topic

Next Topic

In This Chapter

SX10509: Server Xwire data blocks

SX10509: Server example Xwire buffer allocation and configuration

SX10509: Server Commands

SX10509: Server Commands in more detail


SX10509: Server Xwire data blocks

The Network Address handles the vast majority of commands and data between the SPLat controller and the SX10509. It requires an Xwire transmit data block and a receive data block.

Note: This address is set to 64 by default but can be changed in the Xwire Configuration tab.

Xwire network transmit data block:

Memory | Description | Format
0 | TxCommand | Byte
1 | TxLength | Byte
2 | TxData0 | Byte
3 | TxData1 | Byte
... | ... | ...
N | TxDataN | Byte

Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program.

Memory location 0 in the Xwire transmit data block is the TxCommand. The SX10509 performs various actions depending on its value. When the TxLength and TxDataX bytes have all been set, change TxCommand to instruct the SX10509 to take action.

Memory location 1 in the Xwire transmit data block is the TxLength byte. This is set to the number of active bytes of data that follow. The SX10509 will process that many bytes of data when it executes the command in TxCommand.

Memory location 2, 3, … M in the Xwire transmit data block is where data is stored to be transferred to the SX10509. The maximum number of bytes that can be reserved is 30 bytes.

The network transmit data block may be between 3 and 32 bytes long.

Xwire network receive data block:

Memory | Description | Format
0 | CommandEcho | Byte
1 | RxLength | Byte
2 | RxData0 | Byte
3 | RxData1 | Byte
... | ... | ...
M | RxDataN | Byte

Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program.

Memory location 0 in the Xwire receive data block is the CommandEcho byte from the SX10509. The CommandEcho byte will be set to the same value as TxCommand once the SX10509 has completed the command.

Memory location 1 in the Xwire receive data block is the RxLength byte. This is set to the number of bytes of data that follow. The SPLat controller should process this number of bytes, no more.

Memory location 2, 3, … M in the Xwire receive data block is the data received from the SX10509.

The network receive data block may be between 3 and 32 bytes long.


SX10509: Server example Xwire buffer allocation and configuration

Here is an example Xwire buffer allocation and configuration:

;SX10509 NETWORK buffer
                        defBLOCK 32                   ;transmit data block
bTxCmd                  defBYTE                       ;command (bit 7 toggles the next iteration of the current command)
bTxLength               defBYTE                       ;length of transmit data
kTxDataLen              EQU      30
abTxData                defBYTE  30                   ;transmit data

                        defBLOCK 32                   ;receive data block (chose 32 as this is the SX10509 default)

bRxCmdEcho              defBYTE                       ;copy of the last completed command (will match bTxCmd when done)

bRxLength               defBYTE                       ;receive data length

kRxDataLen              EQU      30

abRxData                defBYTE  30                   ;received data

    NVEM0

   ;SX10509 Xwire NETWORK configuration

   NV0Byte     64                                     ;Address of the SX10509 SPLat Call Home board

   NV0Byte     bTxCmd                                 ;Address of the Tx data block

   NV0Byte     32                                     ;Length of the Tx data block

   NV0Byte     bRxCmdEcho                             ;Address of the Rx data block

   NV0Byte     32                                     ;Length of the Rx data block

    NV0Byte	255					   	   ;End of table sentinel.

In this example, 32 bytes have been allocated for the controller transmit data block and 32 for the receive data block.

Hence, on the Xwire Configuration tab for the SX10509, 32 was also entered for the "Controller Network Rx Size" field.


SX10509: Server Commands

The controller communicates with the SX10509 via commands. The following table lists the commands and the data that is associated with each one:

:: Transmit Buffer (Commands) ::
CommandName CommandValue   NextByte          NextBytes
              Byte 0        Byte 1           Bytes 2...n
-----------------------------------------------------------------
Idle        '00 / '80      Unused            Unused
Write       '01 / '81      bLength           Data..
Read        '02 / '82      CumulativeOffset  Unused
Server      '03 / '83      bLength           Data..
Port        '04 / '84      4 bytes of floating point port number
Path        '05 / '85      bLength           Data..
ContentType '06 / '86      bLength           Data..
DoGet       '07 / '87      Unused            Unused
DoPost      '08 / '88      Unused            Unused
GetID       '09 / '89      Unused            Unused
GetGMT      '0A / '8A      Unused            Unused
GetIP       '0B / '8B      Unused            Unused

:: Receive Buffer (responses) ::
CommandName CommandValue   NextByte          NextBytes
              Byte 0        Byte 1           Bytes 2...n
-----------------------------------------------------------------
Idle        '00 / '80      Unused            Unused
ToServer    '01 / '81      Unused            Unused
FromServer  '02 / '82      bLength           Data..
Server      '03 / '83      Unused            Unused
Port        '04 / '84      Unused            Unused
Path        '05 / '85      Unused            Unused
ContentType '06 / '86      Unused            Unused
DoGet       '07 / '87      bLength*          4 bytes of floating point status code
DoPost      '08 / '88      bLength*          4 bytes of floating point status code
GetID       '09 / '89      bLength           IDString..
GetGMT      '0A / '8A      bLength           GMTString..
GetIP       '0B / '8B      IP                IP,IP,IP

* For DoGet and DoPost responses, the bLengthByte is the number of characters available

  for readout using subsequent Read commands

To send a command, first load up any data associated with it, and the bLength byte is required. Then, once all the data is ready, load the command byte itself.

The SX10509 will ignore the data until the command byte is loaded.  Once it sees a new command byte, it will execute the command. When it finishes executing the command it will return a matching command byte in the network receive data block. For most commands this will be nearly instantaneous, but for DoGet and DoPost it may take several seconds.

Be sure to wait for the command byte response before sending the next command.

As the Xwire data blocks are limited to 32 bytes, these commands may require several iterations to be fully completed:

   Write

   Read

   Server

   Path

   ContentType

For example, setting a path with more than 30 characters, like "custom/scripts/mmi202/poopump/flow_monitor.php", must be done in two iterations.

Bit 7 of the command byte may be toggled to indicate the next iteration is ready.  Here's an example:

;Creating two versions of the Path command provides a simple way to toggle

CMDkPathA   EQU   '05   ;Path command

CMDkPathB   EQU   '85   ;(toggled version of the) Path command

   SetMem         bTxByte1,0                          ;set the length to zero to erase any existing path

   SetMem         bTxByte0,CMDkPathA                  ;load the command

WafDoPath1:

   YieldTask

   GoIfMNE        bRxByte0,CMDkPathA,WafDoPath1       ;waf erase to complete

   LoadI          0                                   ;zero the index (this will be used as a length indicator)

   iiPrintText    bTxByte2,"custom/scripts/mmi202/p"  ;store the first part of the path

   ItoX                                               ;get the length

   Store          bTxByte1                            ;store the length of the first part

   SetMem         bTxByte0,CMDkPathB                  ;load the path command (toggled version)

WafDoPath2:

   YieldTask

   GoIfMNE        bRxByte0,CMDkPathB,WafDoPath2       ;waf the SX10509 to save the first part

   LoadI          0                                   ;zero the index (this will be used as a length indicator)

   iiPrintText    bTxByte2,"oopump/flow_monitor.php"  ;store the second part of the path

   ItoX                                               ;get the length

   Store          bTxByte1                            ;store the length of the second part

   SetMem         bTxByte0,CMDkPathA                  ;load the path command

WafDoPath3:

   YieldTask

   GoIfMNE        bRxByte0,CMDkPathA,WafDoPath3       ;waf the SX10509 to save the second part

   ;-- the full path has now been loaded

If, instead of issuing hardcoded strings you're running in a loop, then you can toggle the command with a NotS instruction, for example:

   NotS           7,bTxByte0                          ;toggle the command

Now you can check for command completion by comparing the current Tx command with the Rx command, for example:

   Recall         bTxByte0                            ;get the request

   Recall         bRxByte0                            ;get the response

   Compare                                            ;has the command been performed?

   GoIfNZ         StillWaiting                        ;jump if still waiting

The SX10509 tutorial shows an alternative way of doing this.


SX10509: Server Commands in more detail

Below is a full explanation of the network commands. Click here for a tutorial with examples of using several of the commands.

Idle '00 / '80

Force the SX10509 to the idle state. It's a good idea to send this command at the end of each session to idle the command processor and ensure it's ready for the next series of commands. The Xwire transmit buffer takes the following form:

Command

Once this command is executed, the Xwire receive data block takes the following form:

CommandEcho (Equal to the Command byte sent to the SX10509)

Write '01 / '81

This command is used to load the data that will be sent to the server. If using a GET request, you must ensure the data

is URL escaped according to RFC3986 Section 2 "Characters", see http://tools.ietf.org/html/rfc3986#section-2 .

A POST request has no such restriction allowing data to be sent as is. The Xwire transmit buffer takes the following form:

Command, bLength, Data...

Where:

  • bLength is the length of data in this iteration of the command. Each iteration will append data onto that already stored. Setting bLength to zero will erase all existing data, so this should be done before new data is loaded.
  • Data... is bLength bytes of data to append to that already stored. This is what will be sent to the server.

Once this command is executed, the Xwire receive data block takes the following form:

CommandEcho (Equal to the Command byte sent to the SX10509)

Read '02 / '82

This command may be used to read data returned by the server in response to a DoGet or DoPost. The DoGet and DoPost responses will indicate how much data is available. The Xwire transmit buffer takes the following form:

Command, bOffsetInc

Where:

  • bOffsetInc is the cumulative starting position for the next read. Set this to zero to (re-)start reading from the beginning of the SX10509 network receive buffer. Each time this is set non-zero, the new read position increments by the same amount. Hence, if this is first set to 0, then 10, then 5 on 3 successive reads, the returned data will start from buffer positions 0, 10 and 15, that is, the 16th received byte.

Once this command is executed, the Xwire receive data block takes the following form:

CommandEcho, bLength, Data...

Where:

  • CommandEcho is identical to the command byte sent to the SX10509
  • bLength is the amount of data returned. Zero means there is no data remaining.
  • Data... is bLength bytes of data from the server.

Server '03 / '83

Set the server name where the request will be sent (example: www.splatco.com). Don't use a leading protocol, ie "http://" as this will automatically be appended by the SX10509. The server name must not exceed 63 characters in length. The Xwire transmit buffer takes the following form:

Command, bLength, Data...

Where:

  • bLength indicates how much data is in this iteration of the command. Setting this to zero will erase any existing server string, and should be done as the first step in setting a new server name.
  • Data... is bLength bytes of the server URL.

Consecutive iterations of the Server command will append data to that already sent. This allows a long server string to be constructed.

Once this command is executed, the Xwire receive data block takes the following form:

CommandEcho (Equal to the Command byte sent to the SX10509)

This command is needed only if the server needs to be changed from the power-up default set in the Server tab of the SX10509 configuration screen.

Port '04 / '84

Set the server port where the request should be sent. The Xwire transmit buffer takes the following form:

Command, fPort

Where:

  • fPort is a SPLat floating point value. It must be a whole number and must not exceed 65535. fPort is typically 80 when sending a request to a HTTP server.

Once this command is executed, the Xwire receive data block takes the following form:

CommandEcho (Equal to the Command byte sent to the SX10509)

This command is needed only if the port needs to be changed from the power-up default set in the Server tab of the SX10509 configuration screen.

Path '05 / '85

Set the path to the script on the server. Don't use a leading "/". The path must not exceed 63 characters in length. The Xwire transmit buffer takes the following form:

Command, bLength, Data...

Where:

  • bLength indicates how much data is in this iteration of the command. Setting this to zero will erase any existing path string, and should be done as the first step in setting a new path.
  • Data is bLength bytes of the path.

Consecutive iterations of the Path command will append data to that already sent to the SX10509. This allows a long path string to be constructed.

Once this command is executed, the Xwire receive data block takes the following form:

CommandEcho (Equal to the Command byte sent to the SX10509)

This command is needed only if the path needs to be changed from the power-up default set in the Server tab of the configuration screen.

ContentType '06 / '86

Set the content type (also known as media type) when using a POST request. This is not used when performing a GET request. The content type must not exceed 39 characters in length. The Xwire transmit buffer takes the following form:

Command, bLength, Data...

Where:

  • bLength indicates how much data is in this iteration of the command. Setting this to zero will erase any existing type string, and should be done as the first step in setting a new type.
  • Data is bLength bytes of the content type

Consecutive iterations of the ContentType command will append data to that already sent to the SX10509. This allows a long type string to be constructed.

Once this command is executed, the Xwire receive data block takes the following form:

CommandEcho (Equal to the Command byte sent to the SX10509)

This command is needed only if the content type needs to be changed from the power-up default set in the Server tab of the configuration screen.

DoGet '07 / '87

Send a GET request based on the Server, Port, Path and Data that has previously been loaded into the SX10509.

The Xwire transmit buffer take ths following form:

Command

It may take several seconds for the request to be sent and the reply received. Loading any other command while the GET

request is in progress will cancel the GET request. The Xwire receive data block for the reply takes the following form:

CommandEcho,bLength,fResult

Where:

  • CommandEcho is identical to the command byte sent to the SX10509
  • bLength is the total length of data received from the server, truncated to 255 bytes.
  • fResult is the result as sent from the server. This will be 200 if the request completed successfully, 404 if the script couldn't be found, etc. Refer to RFC2616 section 10 "Status Code Definitions", see http://tools.ietf.org/html/rfc2616#section-10

DoPost '08 / '88

This is just like the DoGet command, except a POST request will be sent instead. For this command, the ContentType string will also be used.

Otherwise, this command is identical to the DoGet command, so please refer to that section for more details.

GetID '09 / '89

This command fetches the unique ID of the SX10509. This ID is fixed and cannot be modified.

The Xwire transmit buffer takes the following form:

Command

On completion of the command the Xwire receive data block will take the form:

CommandEcho,bLength,IDString...

Where:

  • CommandEcho is identical to the command byte sent to the SX10509
  • bLength is the number of characters in the ID string (maximum of 8)
  • IDString is the ID number in ASCII.

To help the server identify the source of each request, this ID is automatically sent to the server in the http header

within a field named "X-SPLat-ID", for example, here's a full POST request sent by the SX10509:

   -------------------------------------
   POST /sch/weather.php HTTP/1.0
   Host: www.splatco.com:80
   Content-Type: application/x-www-form-urlencoded
   Content-Length: 14
   X-SPLat-ID: 3691053277
   Connection: close

   city=melbourne
   -------------------------------------

The "city=melbourne" line was sent by the SPLat application using the Write command.

If your server script is written in PHP, a simple method for the server-side script to obtain this ID is as follows:

$id = $_SERVER['HTTP_X_SPLAT_ID'];

GetGMT '0A / '8A

This command fetches the current epoch time in seconds since 1/1/1970, GMT. This time may be used in a request sent to a server as a timestamp. The Xwire transmit buffer takes the following form:

Command

On completion of the command the Xwire receive data block will take the form:

CommandEcho,bLength,GMTString...

Where:

  • CommandEcho is identical to the command byte sent to the SX10509
  • bLength is the number of characters in the ID string (maximum of 8)
  • GMTString is the GMT number in ASCII.

GetIP '0B / '8B

This command returns the current LAN IP address of the SX10509. The first three bytes are the IP address as 8-bit unsigned values.

For example, an address of 192.168.1.23 is returned as 192,168,123. The Xwire transmit buffer takes the following form:

Command

On completion of the command the Xwire receive data block will take the form:

CommandEcho,IP_1,IP_2,IP_3

Where:

  • CommandEcho is identical to the command byte sent to the SX10509
  • IP_1 is the first 8-bit number (octet) of the IPv4 IP address, in binary.
  • IP_2 is the second 8-bit number (octet) of the IPv4 IP address, in binary.
  • IP_3 is the third 8-bit number (octet) of the IPv4 IP address, in binary.


SX10509: Date and Time

The SX10509 synchronises the date and time on power-up, and every 24 hours, with an internet NTP server. The time and date are very easy to read out, and can be used for example to time stamp data being sent to the server.

The timezone offset for your local time may be set in the Time Configuration tab. If you set it to zero you will get GMT (Zulu time). There is no automatic provision for daylight saving time. You may choose to ignore both the date and time items, or have your controller receive one or both. Just add the address for each one you want to your Xwire configuration table.

The following tables depict how the Xwire data blocks need to appear in memory.

Time Address

The Xwire time address handles the internet synchronised time from the SX10509 real time clock. This is optional and requires its own an Xwire receive data block if used.

Note: This address is set to 65 by default but can be changed in the Xwire Configuration tab.

Xwire time receive data block:

Memory | Description | Format
0 | Hours | Byte
1 | Minutes | Byte
2 | Seconds | Byte

Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory location 0 in the Xwire receive data block corresponds to the 'Hours', this is where the hour count from the SX10509 Real Time Clock will appear.

The 'Hours' byte is an 8-bit integer (binary) in 24-hour format.

Memory location 1 in the Xwire receive data block corresponds to the 'Minutes', this is where the minute count from the SX10509s Real Time Clock will appear.

Memory location 2 in the Xwire receive data block corresponds to the 'Seconds', this is where the second count from the SX10509s Real Time Clock will appear.

Date Address

The Xwire date address handles the internet synchronised Local Date from the SX10509 real time clock. This is optional and requires its own an Xwire receive data block if used.

Note: This address is set to 66 by default but can be changed in the Xwire Configuration tab.

Xwire date receive data block:

Memory | Description | Format
0 | Day of the Week | Byte
1 | Day of the Month | Byte
2 | Month | Byte
3 | Year | Byte

Note that this data block does not need to start at memory location 0. The data block can be moved anywhere in memory as long as it stays together, is in the order above and is defined in the NVEM Xwire table in the program code.

Memory location 0 in the Xwire date receive data block corresponds to the 'Day of the Week', this is where the day of the week from the SX10509's Real Time Clock will appear.
The byte will contain a binary number 0-6. 0 = Sunday. 6 = Saturday.

Memory location 1 in the Xwire receive data block corresponds to the 'Day of the Month', this is where the day of the month from the SX10509's Real Time Clock will appear.
The byte ranges from 1 thru 31 depending upon the number of days in the month.

Memory location 2 in the Xwire receive data block corresponds to the 'Month', this is where the month from the SX10509's Real Time Clock will appear.
The month ranges from 1 (January) thru 12 (December).

Memory location 3 in the Xwire receive data block corresponds to the 'Year', this is where year from the SX10509's Real Time Clock will appear. This is the year since 2000, so if a number of 10 was recalled from the 'Year' byte it would mean it's 2010.

Time Xwire buffer example:

To access this time, setup your SPLat controller with a zero length transmit buffer and a 3 byte receive buffer, for example:

;SX10509 TIME buffer
               defBLOCK 3     ;receive buffer
bHour          defBYTE
bMin           defBYTE
bSec           defBYTE

   NVEM0

   ;SX10509 Xwire TIME configuration

   NV0Byte     65            ;Address of the SX10509 SPLat Call Home board, TIME

   NV0Byte     0             ;Address of the Tx buffer (placeholder ... there is none)

   NV0Byte     0             ;Length of the Tx buffer (there is none)

   NV0Byte     bHour         ;Address of the Rx buffer

   NV0Byte     3             ;Length of the Rx buffer

   NV0Byte 255               ;End of table sentinel.

As shown in the example above, the time will be returned in 3 bytes as hours, minutes and seconds.  Hours ranges from 0 thru 23, that is, 24 hour time.

Date Xwire buffer example:

To access this time, setup your SPLat controller with a zero length transmit buffer and a 4 byte receive buffer, for example:

;SX10509 DATE Buffer

                defBLOCK 4     ;receive buffer

bWDay           defBYTE

bMDay           defBYTE

bMon            defBYTE

bYear2000       defBYTE

   NVEM0

   ;SX10509 Xwire DATE configuration

   NV0Byte     66             ;Address of the SX10509 SPLat Call Home board, DATE

   NV0Byte     0              ;Address of the Tx buffer

   NV0Byte     0              ;Length of the Tx buffer

   NV0Byte     bYear2000      ;Address of the Rx buffer

   NV0Byte     4              ;Length of the Rx buffer

   NV0Byte      255           ;End of table sentinel.

As shown in the example above, the date will be returned in 4 bytes as day of week, day of month, month and year since 2000.


SX10509: Example Programs
==============================================================================
= Sample program gets and display the IP address of the SX10509.  Runs on    =
= either a MS120 or MMi202                                                   =
==============================================================================
;--Network Commands
kCMD_Idle         EQU      '00
kCMD_WriteA       EQU      '01
kCMD_WriteB       EQU      '81
kCMD_ReadA        EQU      '02
kCMD_ReadB        EQU      '82
kCMD_ServerA      EQU      '03
kCMD_ServerB      EQU      '83
kCMD_Port         EQU      '04
kCMD_PathA        EQU      '05
kCMD_PathB        EQU      '85
kCMD_ContentTypeA EQU      '06
kCMD_ContentTypeB EQU      '86
kCMD_DoGet        EQU      '07
kCMD_DoPost       EQU      '08
kCMD_GetID        EQU      '09
kCMD_GetGMT       EQU      '0A
kCMD_GetIP        EQU      '0B

;SX10509 NETWORK buffer
                  defBLOCK 32        ;transmit buffer
bTxCmd            defBYTE            ;command (bit 7 toggles the next iteration of the current command)
bTxLength         defBYTE            ;length of transmit data
kTxDataLen        EQU      30
abTxData          defBYTE  30        ;transmit data

                  defBLOCK 32        ;receive buffer (chose 32 as this is the SX10509 default)
bRxCmdDone        defBYTE            ;copy of the last completed command (will match bTxCmd when done)
bRxLength         defBYTE            ;receive data length
kRxDataLen        EQU      30
abRxData          defBYTE  30        ;received data

   XwireMaster    pXwireTable        ;run the controller as an Xwire master
   OBLCD_Cls                         ;clear the LCD
   OBLCD_Text     "Requesting..."    ;display a message
_RequestIP:
   LoadX          kCMD_GetIP         ;ask the SX10509 for..
   Store          bTxCmd             ;..it's IP address
_WAFIP:
   GoIfMNE        bRxCmdDone,kCMD_GetIP,_WAFIP ;loop until we get a reply
   OBLCD_Cls                         ;clear the screen
   OBLCD_Text     "IP:"              ;display the title
   OBLCD_SetCur   1,0                ;next line
   LoadI          0                                   ;start with the first IP byte
_IPPrintLoop:
   iRecall        bRxLength         ;get the IP byte
   OBLCD_UDecDispXVW                ;display the number
   IncI                             ;move the index to the next IP byte
   ItoX                             ;have we displayed all..
   GoIfXEQ        4,_EndIPPrint     ;..the bytes?  jump if yes.
   OBLCD_Text     "."               ;otherwise more bytes to display, so add a '.' separator
   Goto           _IPPrintLoop      ;display the next byte
_EndIPPrint:
   LoadX          kCMD_Idle         ;optional, inform the controller..
   Store          bTxCmd            ;..we've finished (will allow us to ask for the IP again)
   Pause          10                ;sleep for a bit
   Goto           _RequestIP        ;request the IP again

;============================================================
   NVEM0                                              ;start of NVM data

pXwireTable
   ;SX10509 Xwire NETWORK configuration
   NV0Byte     64                   ;Address of the SPLat Call Home board
   NV0Byte     bTxCmd               ;Address of the Tx buffer
   NV0Byte     32                   ;Length of the Tx buffer
   NV0Byte     bRxCmdDone           ;Address of the Rx buffer
   NV0Byte     32                   ;Length of the Rx buffer

   NV0Byte     255


SX10509: Tutorial and online sandbox

A detailed programming tutorial with a matching online practice website (sandbox) is located here.


Product documentation: Bluetooth adaptors

Bluetooth adaptors for SPLat controllers convert the serial (programming) port for use over Bluetooth. Its main intended use is to allow a portable device such as a smart phone or tablet computer to be used as an operator interface for a SPLat controller. To this end we have developed Human-Machine Interface (HMI) program for Android, called SimpleHMI. SimpleHMI is supported in SPLat/PC by the HMI hash command and associated hash functions.

At time of writing (May 2012) you cannot program a SPLat via Bluetooth.

Pairing with a Bluetooth adaptor

Before you can use an adaptor on your Android device, it has to be "paired" with the device (this is a bit like a formal introduction in a Jane Austen novel, without which they cannot talk to each other). The reverse side of the adaptor has a serial number label. This serial number is used to identify the adaptor in your Android device (which I will assume is a phone, but it could also be a tablet or something not invented at time of writing).

The following applies equally to the BT-TTL5 and the BT-RS232 adaptors:

To pair with the adaptor, install it on your SPLat controller, and power up (there's no need for a program in the SPLat, but avoid having a program in it that uses serial communications).

You should get a fast flashing green LED.

The following procedure will get the adaptor paired to your phone. The details will vary between brands of phone and versions of Android.

On the phone select Settings:

In Settings select Wireless and networks. In Wireless and networks select Bluetooth settings. In Bluetooth settings make sure Bluetooth in enabled. Now select Scan devices

The phone should "grind away" for a while, then your adaptor should appear, like above.

Select the adaptor to pair with it. You should get a prompt asking for the adaptor's PIN. The PIN is "1234". Enter the PIN (tap the data entry field to get a keypad) and select OK.

The phone should revert to the Bluetooth settings screen, with the adaptor now shown as paired with your phone. On the adaptor you should have a slow flashing green LED. That means it can be connected to an app, for example SimpleHMI.

Connecting

When your phone, via an app such as SimpleHMI, connects to the adaptor, it will show a red LED plus a slow double flash on the green LED. When the phone disconnects again, it will revert to a fast green flash (no red). It is a good idea to experiment connecting and disconnecting a few times to get a feel for the various indications.

Previous Topic

Next Topic

In This Chapter

BT-TTL5 - 5V TTL Bluetooth adaptor

BT-RS232 Bluetooth adaptor


BT-TTL5 - 5V TTL Bluetooth adaptor

The BT-TTL5 adds Bluetooth serial communications to your compatible SPLat controller. It works with any SPLat controller that has suitable firmware and a 4-pin programming connector using 5V levels. This includes CC18, MS120, HD8, HVAC26 and numerous custom controllers. It is not suitable for controllers that use 3.3V or RS232 voltage levels. If in doubt about your controller, please contact us.

This instruction assumes you are using the BT-TTL5 to connect a SPLat controller to an Android device. The BT-TTL5 is a slave-only device using Bluetooth Serial Port Protocol (SPP).

Do not rely on Bluetooth communications to maintain the safety of a system. It does not provide a secure connection.

Installation

The BT-TTL5 simply plugs onto the controller's 4-pin programming connector. Note that the 4-pin female connector on the adaptor has two lugs. These are there simply to secure the connector to the adaptor board, and are not for locating the adaptor on the controller board. When you plug the adaptor onto the controller you must make sure the pins are properly aligned.

The following picture shows the BT-TTL5 on a CC18. Note the orientation of the components relative to the connector on the controller.

On the MS120 you have to gently bend the programming connector to about a 30 degree angle from the board, in order for the adaptor to fit on the connector. You won't be able to that if you are using a SPice board on your MS120. In that case you need to make up an extension cable up to 20cm long and run the adaptor off-board. You can also use a BT-RS232 adaptor, which has mounting holes.

Communications parameters

The adaptor is factory pre-set to 38,400 baud, 8 data bits, no parity.

Power supply considerations

The BT-TTL5 will draw more current off the controller than the controller was initially designed for (after all, we designed the controllers long before a Bluetooth adaptor was contemplated). In most cases this will fall within the conservative design of our controllers, especially at temperatures below the maximum rated for the controllers (normally 60°C). At high temperatures there is a slight chance that the regulator on the controller will shut down on thermal overload (the controller will stop working but recover when it cools down). We suggest you consider that the use of external adaptors on standard SPLat controllers is for Proof of Concept work. If you want to deploy them in your product on an ongoing basis please either consult us with your specific operating conditions, or consider a custom SPLat that has been designed for the purpose.

Radio propagation, range

The BT-TTL5 is Class 2 Bluetooth, which has a range of up to 10m in free air, line of sight. The "snake" pattern on the end of the board is the antenna. This needs to be kept away from any metal surfaces, and the whole unit should not be enclosed in metal.

You can maximise the range by making sure the adaptor is in an elevated position, clear of any surrounding metal. You can use up to 20cm of extension cable.


BT-RS232 Bluetooth adaptor

The BT-RS232 adds Bluetooth serial communications to your compatible SPLat controller. It works with any SPLat controller that has suitable firmware and a 9-pin programming connector using RS232 voltage levels. This includes SL100, MMI202 and custom controllers. It is not suitable for controllers that use 3.3V or 5V levels. If in doubt about your controller, please contact us.

This instruction assumes you are using the BT-RS232 to connect a SPLat controller to an Android device. The BT-RS232 is a slave-only device using Bluetooth Serial Port Protocol (SPP).

Do not rely on Bluetooth communications to maintain the safety of a system. It does not provide a secure connection.

Installation

The BT-RS232 simply plugs onto the controller's 9-pin DB9 programming connector.

Communications parameters

The adaptor is factory pre-set to 38,400 baud, 8 data bits, no parity.

Power supply considerations

The BT-RS232 draws its 5V power from the programming connector. It will draw more current off the controller than the controller was initially designed for (after all, we designed the controllers long before a Bluetooth adaptor was contemplated). In most cases this will fall within the conservative design of our controllers, especially at temperatures below the maximum rated for the controllers (normally 60°C). At high temperatures there is a slight chance that the regulator on the controller will shut down on thermal overload (the controller will stop working but recover when it cools down). We suggest you consider that the use of external adaptors on standard SPLat controllers is for Proof of Concept work. If you want to deploy them in your product on an ongoing basis please either consult us with your specific operating conditions, or consider a custom SPLat that has been designed for the purpose.

Radio propagation, range

The BT-RS232 is Class 2 Bluetooth, which has a range of up to 10m in free air, line of sight. The "snake" pattern on the end of the board is the antenna. This needs to be kept away from any metal surfaces, and the whole unit should not be enclosed in metal.

You can maximise the range by making sure the adaptor is in an elevated position, clear of any surrounding metal. You can use up to 20cm of extension cable.


Product documentation: RS485 adaptors

Previous Topic

Next Topic

In This Chapter

X485: X-Wire to RS485 adaptor for CC18

XP485: X-Wire to RS485 adaptor

TL485/MS485: RS485 adaptor for CC16/CC18/MS120/HD8

MS485: RS485 adaptor for MS120

PC485: RS232 to RS485 adaptor

MB485:


X485: X-Wire to RS485 adaptor for CC18

The X485 is an X-Wire to RS485 adaptor designed specifically for the CC18 controller. The X485 allows X-Wire communications protocol to be transmitted over RS485.
It plugs directly onto the CC18 board's X-Wire and Analog connectors, and gets its power from the CC18.

Features:

* Easy to install

* 2-wire RS485

* Up to 32 devices on a single RS485 circuit

* Automatic transmit/receive (line direction) control

Previous Topic

Next Topic

In This Chapter

X485: Installation

X485: Wiring

X485: Notes


X485: Installation

The picture below shows a X485 fitted to a CC18

To fit the adaptor to your controller, simply plug it onto the Analog and X-Wire connectors as shown. Make sure you align the pins on the two connectors properly.


X485: Wiring

RS485 circuits contain 2 wires, A and B. The adaptor has screw terminals for A and B, plus one labelled 0V. The latter is connected to ground (0V) of the controller. If you are using screened cable (2 cores + shield), connect the shield to 0V.

With an RS485 hookup all A terminals on all devices should be connected together and all B wires should be connected together. If you should swap A and B somewhere the system will not work. For long runs it pays to use shielded twisted pair cable. In that case the shield should connect to the 0V or Gnd terminal of each RS485 device.


X485: Notes

A system may contain up to 32 RS485 devices on one pair of wires. Typically distances of many 100's of metres can be realised with RS485, and with the relatively low data rate of Xwire (38.4 Kbaud), performance of the connecting wires is relaxed. It is impossible to actually specify the maximum distance over which reliable data transfer can be achieved, as there are unknowns that affect the performance, such as the amount of electrical noise and the connecting cables electrical characteristics.

Also, to minimise ground currents through the 0V connection, it is advisable to either have the controllers and Xwire peripherals all isolated from mains earth, or to only ground one controller at one location (usually the Xwire master). The X485 is fitted with transient suppression diodes to prevent damage due to strong electrical noise on the A and B data lines.


XP485: X-Wire to RS485 adaptor

The XP485 is an X-Wire to RS485 adaptor designed for use with any X-Wire capable SPLat controllers. The XP485 allows X-Wire communications protocol to be transmitted over RS485.
It connects to the SPLat controller via its X-Wire connector.

Features:

* Easy to install

* 2-wire RS485

* Up to 32 devices on a single RS485 circuit

* Automatic transmit/receive (line direction) control

Previous Topic

Next Topic

In This Chapter

XP485: Installation

PC485: Power supply

XP485: Wiring

XP485: Notes


XP485: Installation

To fit the adaptor to your controller, simply connect the X-Wire connector from your SPLat controller to the X-Wire connector on the XP485.


PC485: Power supply

The XP485 needs a DC power supply of between 8V and 24V. The power supply must be connected to the pair of screw terminals labelled +V (positive) and 0V (negative).


XP485: Wiring

RS485 circuits contain 2 wires, A and B. The adaptor has screw terminals for A and B, plus one labelled 0V. The latter is connected to ground (0V) of the controller. If you are using screened cable (2 cores + shield), connect the shield to 0V.

With an RS485 hookup all A terminals on all devices should be connected together and all B wires should be connected together. If you should swap A and B somewhere the system will not work. For long runs it pays to use shielded twisted pair cable. In that case the shield should connect to the 0V or Gnd terminal of each RS485 device.


XP485: Notes

A system may contain up to 32 RS485 devices on one pair of wires. Typically distances of many 100's of metres can be realised with RS485, and with the relatively low data rate of Xwire (38.4 Kbaud), performance of the connecting wires is relaxed. It is impossible to actually specify the maximum distance over which reliable data transfer can be achieved, as there are unknowns that affect the performance, such as the amount of electrical noise and the connecting cables electrical characteristics.

Also, to minimise ground currents through the 0V connection, it is advisable to either have the controllers and Xwire peripherals all isolated from mains earth, or to only ground one controller at one location (usually the Xwire master). The XP485 is fitted with transient suppression diodes to prevent damage due to strong electrical noise on the A and B data lines.


TL485/MS485: RS485 adaptor for CC16/CC18/MS120/HD8

The TL485 is an RS485 interface designed specifically for the CC18 and HD8 controllers. It plugs directly onto the controller board's Comms connector, and gets its power from the board.

The MS485 is the same board with a slightly different connector to plug into the MS120 controller.

Features:

  • Easy to install
  • 2-wire RS485
  • Up to 32 devices on a single RS485 circuit
  • Automatic transmit/receive (line direction) control
  • ModBus compatible

The TL485 will work only with half duplex communication protocols like ModBus RTU. It uses start bit detection to switch from receive mode to transmit mode. This uses a fixed interval timer that will only work correctly at 9600 BPS.

This device is intended primarily for Proof of Concept work. It does not contain protection circuitry for use in an industrial environment, and is only loosely mounted to the controller by a friction lock connector.

Previous Topic

Next Topic

In This Chapter

TL485/MS485: Installation

TL485/MS485: Wiring


TL485/MS485: Installation

The pictures below shows a TL485 fitted to a CC18 (left) and MS485 fitted to an MS120 (right). The two adaptors are the same board with different connectors.

To fit the adaptor to your controller, simply plug it onto the Comms connector as shown. Make sure you align the pins on the two connectors properly.

For a permanent installation you can glue the adaptor to the controller using hot melt glue or neutral cure silicone (do not use the stuff that smells of vinegar!). Do keep in mind, however, that to re-program the controller you need to disconnect the adaptor and connect the PC232 programming cable. The controller cannot be reprogrammed via RS485.


TL485/MS485: Wiring

RS485 circuits contain 2 wires, A and B. The adaptor has screw terminals for A and B, plus one labelled 0V. The latter is connected to ground (0V) of the controller. If you are using screened cable (2 cores + shield), connect the shield to 0V.

With an RS485 hookup all A terminals on all devices should be connected together and all B wires should be connected together. If you should swap A and B somewhere the system will not work. For long runs it pays to use shielded twisted pair cable. In that case the shield should connect to the 0V or Gnd terminal of each RS485 device.

A system may contain up to 32 RS485 devices on one pair of wires.


MS485: RS485 adaptor for MS120

Please see TL485/MS485


PC485: RS232 to RS485 adaptor

The PC485 is designed to convert a computer (PC) serial RS232 COM port to RS485 for use with SPLat controllers. It's primary application is systems that use ModBus protocol to connect one or more SPLat controllers to a PC.

The PC485 will work only with half duplex communication protocols like ModBus RTU. It uses start bit detection to switch from receive mode to transmit mode. This uses a fixed interval timer that will only work correctly at 9600 BPS.

Previous Topic

Next Topic

In This Chapter

PC485: Installation

PC485: Power supply

PC485: RS485 wiring

PC485: Line terminating resistor


PC485: Installation

The PC485 should be wired to a COM port on your PC using a DB9F to DB9M serial cable. It also needs a power supply.


PC485: Power supply

The PC485 needs a DC power supply of between 10V and 24V at 60mA maximum. The current can go up to 200mA if the RS485 line is shorted out. The power supply must be connected to the pair of screw terminals labeled +V (positive) and 0V (negative).

Be sure to get the power supply connections right. There is no reverse polarity protection on this board!


PC485: RS485 wiring

RS485 circuits contain 2 wires, A and B. The PC485 has screw terminals for A and B, plus one labelled G (for Gnd). If you are using screened cable (2 cores + shield), connect the shield to G.

With an RS485 hookup all A terminals on all devices should be connected together and all B wires should be connected together. If you should swap A and B somewhere the system will not work. For long runs it pays to use shielded twisted pair cable. In that case the shield should connect to the 0V or Gnd terminal of each RS485 device.

A system may contain up to 32 PC485s or similar devices on one pair of wires.


PC485: Line terminating resistor

The 2-pin jumper designated LK1, close to the RS485 terminals, selects an optional 100 Ohm line terminating resistor. Normally this jumper should be installed, though at the low data rates we are working with here it will usually make no difference.


MB485:

The MB485 is an RS485 adaptor designed for use on selected SPLat controllers that have an RS232 port with a DB9F connector. It is compatible with MMi203, MMi202, MMi201, and SL100. It will not work with earlier models but should work with future models. It is limited to 9600BPS.

Previous Topic

Next Topic

In This Chapter

MB485: Installation

MB485: Wiring


MB485: Installation

The MB485 simply plugs onto the DB9 connector on the controller. For added stability you can use plastic cable ties to hold the mated connectors together.


MB485: Wiring

RS485 circuits contain 2 wires, A and B. The MB485 has screw terminals for A and B, plus one labelled 0V. The latter is connected to ground (0V) of the controller. If you are using screened cable (2 cores + shield), connect the shield to 0V.

With an RS485 hookup all A terminals on all devices should be connected together and all B wires should be connected together. If you should swap A and B somewhere the system will not work. For long runs it pays to use shielded twisted pair cable. In that case the shield should connect to the 0V or Gnd terminal of each RS485 device.

A system may contain up to 32 MB485s or similar devices on one pair of wires.


Product documentation: I/O adapter boards

I/O adapter boards extend the capabilities of the inputs and outputs on controllers or I/O expanders, by for example converting transistor outputs to relays. They do not increase the number of I/O points.

Previous Topic

Next Topic

In This Chapter

RLX8 product documentation


RLX8 product documentation

Previous Topic

Next Topic

In This Chapter

RLX8: Introduction

RLX8: Power requirements

RLX8: Connecting to a SPLat controller

RLX8: Output relay information


RLX8: Introduction

This document provides information on the use of the SPLat RLX8 relay board. The RLX8 is a board that converts 8 existing solid state outputs on a SPLat board into relay outputs.

Old versus New RLX8 boards

This document applies to the RLX8 board revision introduced in February 2002 (and later revisions). It does not apply to the previous revisions (those boards were supplied with a printed instruction sheet). The new RLX8 revision is a redesign carried out mainly to improve compatibility with the new MMi99 controller.

The revision number is a 4-digit date code located just below the board designation "RLX8". The code is in the form YYMM he earliest revision this document applies to is 0202 (February 2002). The previous revision is 0003 (March 2000).

The old board revision is most easily recognized by the presence of oval shaped component positions located between the screw terminals and the relays, with labels "V1" through "V10". A few very early boards were shipped with blue disk shaped MOV devices fitted in those positions. Newer board revisions have no place for components between the screw terminals and the relays.

Do not use this document with RLX8 boards date coded earlier than YM002


RLX8: Power requirements

The RLX8 draws its power from the same power supply as the SPLat controller board it is attached to. It must be powered off the same power supply.

Do not attempt to power the RLX8 board off a different power supply than the SPLat controller. It will not work, and could damage either or both boards.

The board uses 12VDC relay coils. Associated with each relay is a 12V regulator chip. This ensures that even on a 24VDC system power supply the relay coils will not overheat.

The minimum system supply voltage recommended to ensure reliable relay operation is 15VDC.

Each relay circuit, when activated, draws a maximum of 47mA, independent of the supply voltage. You therefore need to budget that much, times the maximum number of relays your program will turn on simultaneously.

Do not use this document with RLX8 boards date coded earlier than YM002. See Introduction


RLX8: Connecting to a SPLat controller

In the following pages you will find details of connecting the RLX8 to various models of SPLat controller using the UBCC1030 cable supplied with the RLX8DK.

Do not use this document with RLX8 boards date coded earlier than YM002. See Introduction

Previous Topic

Next Topic

In This Chapter

UBCC1030 Connection cable

RLX8: Connecting to MMi99

RLX8: Connecting to OEM32/36

RLX8: Connecting to SL88


UBCC1030 Connection cable

The RLX8DK (the Developer's Kit package) is supplied with a cable assembly called UBCC1030. This cable is deliberately shipped partially assembled, to allow you to finalize the assembly according to your needs.

In the UBCC1030 pack you will find:

  • Two 5-way female connector housings.
  • A 10-way female connector with 9 wires installed. The opposite ends of the wires have contacts crimped onto them. The wires are colour coded in the following sequence (listed with corresponding pin numbers in the 10-way connector)
Pin # | Colour | Function
1 | Black | 0V (not used on RLX8)
2 | Brown | Relay 1
3 | Red | Relay 2
4 | Orange | Relay 3
5 | Yellow or pink | Relay 4
6 | Green | Relay 5
7 | Blue | Relay 6
8 | Purple | Relay 7
9 | Grey | Relay 8
10 | White | Supply voltage (positive)

In the following sections you will find instructions on how to utilize the connection cable, depending on which SPLat controller board you are using. At that time you will allocate the lose contacts to positions within the 5-way housings. Before you go there, however, you should familiarize yourself with a couple of points:

  1. How the female cable connectors plug onto the board. Along one edge of the female connector there is a molded in protrusion (or on some brands, a series of small protrusions, one per contact). When the connector is plugged onto the board, those protrusions should be oriented towards the vertical retaining section on the board-mounted male connector.
  2. How the contacts fit in the housings. If you look carefully at the contacts, you will discover a very small bent out tag or barb. This matches an elongated slot in the side of the plastic housing, such that when the contact is inserted into the housing it clicks into place and can't be pulled out again. It is very important that the contact is fully inserted into the housing, with the barb properly clicked in.


RLX8: Connecting to MMi99

The MMi99 has two 5-pin connectors for its outputs. Each connector has 4 outputs plus a "convenience" connection to the board's positive supply voltage. The supply pins are labeled with a "+" symbol. Please take a moment to identify them. They are at opposite ends of the two output connectors.

The basic principles of connecting an RLX8 to an MMi99, using the UBCC1030 cable, are:

  1. The RLX8 board is to get its required power from the + supply voltage pin on one of the two MMi99 output connectors.
  2. The relay drive pins on the RLX8 board are to be connected to the appropriate MMi99 output pins. When the MMi99 output turns on, it connects the RLX8 relay pin to 0V and completes the power circuit around the relay coil.
  3. There is no need for a ground (0V) connection between the MMi99 and the RLX8.

Before you start inserting pins into housings, it's a good idea to have a plan for allocating RLX8 relays to MMi99 outputs.

Do not use this document with RLX8 boards date coded earlier than YM002. See Introduction


RLX8: Connecting to OEM32/36

The OEM32/36 has one 10-pin plugs that has 8 outputs on it, and two 10-pin plugs that each has 8 bidirectional I/O pins. The bidirectional pins can each be programmed as an input or as an output, so each one is capable of driving an RLX8 relay.

Each of the 10-pin connectors also has a positive supply pin and a 0V (labeled GND) pin. Take a few moments to locate the connectors and power pins.

The basic principles in connecting an RLX8 to an OEM32/36, using the UBCC1030 cable, are:

  1. The RLX8 board is to get its required power from the + supply voltage pin on one of the OEM32/36 output or I/O connectors. That means at least one of the 5-way female connectors must be located at the "+" end of a 10-pin connector, with the wire connected to the "+" pin.
  2. The second 5-way female connector does not have to be on the same OEM32/36 connector as the one with the white "+" wire.
  3. The relay drive pins on the RLX8 board are to be connected to the appropriate OEM32/36 output pins (or I/O pins used as outputs). When the OEM32/36 output turns on, it connects the RLX8 relay pin to 0V and completes the power circuit around the relay coil.
  4. There is no need for a ground (0V, GND) connection between the OEM32/36 and the RLX8.

Before you start inserting pins into housings, it's a good idea to have a plan for allocating RLX8 relays to OEM32/36 pins.

Do not use this document with RLX8 boards date coded earlier than YM002. See Introduction


RLX8: Connecting to SL88

The SL88 has an 8-way screw terminal block for its outputs.

The basic principles in connecting an RLX8 to an SL88, using the UBCC1030 cable, are:

  1. Cut the crimp terminals off the lose ends of the UBCC1030 cable and strip the wires for insertion in the screw terminals.
  2. Connect the white wire to the positive supply voltage of the SL88. The safest thing is to terminate it on the actual power supply terminal block of the SL88.
  3. Allocate relay wires (all but the black wire) to the output terminals of the SL88.

Do not use this document with RLX8 boards date coded earlier than YM002. See Introduction


RLX8: Output relay information

Previous Topic

Next Topic

In This Chapter

RLX8: Relay ratings

RLX8: Output connections


RLX8: Relay ratings

The relays contact ratings depend on whether they are switching AC or DC, and differ for normally open and normally closed contacts.

For resistive loads the ratings are:

Normally open: 5A @ 125VAC, 3A @ 250VAC, 5A @ 30VDC

Normally closed: 2A @ 125VAC, 1A @ 250VAC, 1A @ 30VDC

The above ratings are for resistive loads. If you use the board with inductive loads such as motors or solenoids, there will be additional strain placed on the relay contacts by the spark generated when the contact opens. Derate the contacts by 40%.

If you are driving DC inductive loads (say a contactor with a 24VDC coil) you can reduce the strain on the relay contacts by connecting a diode across the load, cathode to the positive load terminal. This will prevent the spark from forming, at the expense of slowing down the turn-off speed, and allow you to use the relay to the full resistive load rating. A 1N4004 will work fine for any load the relay is able to drive. (Don't attempt this with AC loads!).

It is generally assumed that for AC loads a resistive-capacitive snubber will reduce the damage due to arcing. In our experience with comprehensive tests of driving a large contactor, this is not necessarily true. We found the R-C snubber actually did more harm than good. There is some excellent material here on this and other relay related topics.

The full data sheet for the relays is located in the file resources area of the SPLat Resources CD. Look for jzc33f.pdf


RLX8: Output connections

There are 4 groups of screw terminal blocks for the output relays. Two of the groups each represent one relay, while the other two groups each represent three relays.

Relays 1 and 2 each have their own groups of 3 terminals. For relay 1 these are labeled NO1, C1 and NC1. C1 is the common, NO1 is the normally open contact and NC1 is the normally closed contact. This means that:

  • When the relay is OFF (not driven on by the SPLat controller), C1 is connected to NC1 while NO1 goes nowhere.
  • When the relay is driven ON by the SPLat controller C1 is connected to NO1 while NC1 goes nowhere.

The logic for relay 2 is identical.

Note: Because the NC contacts are held closed in the de-energised state by a spring, we have found that they and are likely to open momentarily if bumped.

Relays 3, 4 and 5 share a single common terminal (C3,4,5), and have only their normally open (NO3, NO4, NO5) contacts accessible. That means whatever these three relays are driving must share a power supply terminal.

Relays 6, 7 and 8 share a single common terminal (C6,7,8), and have only their normally open (NO6, NO7, NO8) contacts accessible. That means whatever these three relays are driving must share a power supply terminal.


Product documentation: Onboard peripherals

Onboard peripherals are devices that are "tightly integrated" into the main processor on a SPLat controller board. "Tightly integrated" means basically that the SPLat processor contains the Firmware required to control the device. Very often an onboard peripheral will be physically located on the controller board, but it may also be on the end of a cable.

Previous Topic

Next Topic

In This Chapter

Onboard LCD

Onboard counter type A

OnBoard Counter type B

Onboard Quadrature Counter

The CPU device


Onboard LCD

Previous Topic

Next Topic

In This Chapter

OBLCD: Introduction

OBLCD: Hardware

OBLCD: Special characters

OBLCD: Programming

OBLCD: ModBus interaction

OBLCD: Support in SPLat/PC


OBLCD: Introduction

This guide covers the use and programming of a Liquid Crystal Display (LCD) associated with the following products:

  • MMi99
  • MMi20x
  • MS12
  • Various custom SPLat controllers.


OBLCD: Hardware

This chapter covers the hardware aspects of the display, i.e. anything not to do with programming it.

Previous Topic

Next Topic

In This Chapter

OBLCD: Buying an LCD module

OBLCD: Connecting the LCD to the SPLat board

OBLCD: Contrast adjustment

OBLCD: Power supply

OBLCD: Backlighting


OBLCD: Buying an LCD module

For small quantities the simplest thing to do is to buy an LCD module as a SPLat accessory product. It will be pre-wired to plug into your SPLat controller, saving you time and effort.

If you are using a lot of LCD modules, or if you want a size we don't supply, you may decide to buy your own LCD modules directly from the supplier. See the OBLCD_Type instruction for a definition of the types supported.

A few manufacturers make LCD modules with a serial interface. That is not the type SPLat requires. SPLat requires the much more common, and cheaper, parallel or microprocessor interface.

There are a very large number of manufacturers of parallel interface LCD modules, and most manufacturers give you a large choice of models. They all share the same logical interface (i.e. which signals they require and how those signals work), but physical size and pinouts can vary. Connecting user supplied LCD module describes how to correctly identify the pins and make connections.

LCD modules come with optional backlighting. SPLat does not provide support circuitry for backlighting, but you can do your own if you have the basic electronic skills. There is some information in Backlighting.

We cannot possibly list all manufacturers and models of suitable LCD modules. Your best bet is to call a few local electronic component distributors, you will soon identify a number of choices.

From May 2009 we will be gradually introducing backlit LCD modules that are capable of being dimmed by the MMi202 with Firmware V3.21 or later.


OBLCD: Connecting the LCD to the SPLat board

This section covers the electrical connection between a SPLat board and an LCD module connected to its LCD port.

This covers the case of a SPLat board with a connection (port) for a bare LCD module. Such a port will be labeled "LCD" and will have a 10-pin box header, as illustrated. It will also have a contrast adjustment potentiometer.

LCD Port picture

In This Section

OBLCD: Connecting SPLat supplied LCD module

OBLCD: Connecting user supplied LCD module


OBLCD: Connecting SPLat supplied LCD module

The LCD modules supplied as accessories to standard SPLat boards come already fitted with a ribbon cable and the appropriate connector. Simply plug it into the matching 10-pin header on the SPLat board and you are ready to go.


OBLCD: Connecting user supplied LCD module

You may elect to source your own LCD module. The onboard LCD connector can drive commercially available modules with the capacities listed under the OBLCD_Type instruction.

If you are sourcing your own LCD you will need to fit it with a ribbon cable and an IDC connector. To achieve this you will need the appropriate skills to identify the necessary connections, to do the fine soldering required and to crimp on the connector.

  1. Obtain the technical data on the exact LCD module you are using. Pinouts vary from manufacturer to manufacturer and from model to model. You must have the right data.
  2. Terminate a length of 10-way ribbon cable in a 10-way IDC (female) connector as illustrated. Make sure the coloured trace on the cable lines up with the pin 1 identification mark on the connector (an arrow head on the connector in the picture). The wires are numbered 1, 2, 3 ... 10 straight across the cable, starting with the coloured wire as #1.
    LCD connector pic
  3. Identify the named connection points on the LCD module. LCD modules come with two connection patterns. One is a single row of holes along the long edge. The other is a dual row of holes along one short edge.In our experience the 2-row patterns vary significantly between brands, including supply connections being reversed. Get it wrong and you will trash your LCD instantly!
  4. Print out the table below, then fill in the "LCD pin" column with the pin numbers on the LCD module, using the signal names to identify the pins. Signal names may vary slightly from brand to brand, but are generally similar enough to be clearly identifiable. Note that our port does not use all the LCD pins.
  5. Carefully separate the wires of the ribbon cable and solder them into the LCD module according to the table.
  6. Check and re-check.
  7. Plug into the SPLat board and apply power.
  8. Use the following program to send a simple message to the LCD, then adjust the contrast for best viewing:

         OBLCD_Text  "Hello World!"

Loop     GoTo        Loop

Connection table:

Cable wire # | Name | LCD pin #
1 | Vss |
2 | Vdd |
3 | RS |
4 | Vo |
5 | E |
6 | R/W |
7 | DB5 |
8 | DB4 |
9 | DB7 |
10 | DB6 |


OBLCD: Contrast adjustment

The SPLat board contains a small adjustable potentiometer close to the LCD (for boards with the LCD mounted on the main SPLat board) or close to the LCD connector. This will be marked with the word "Contrast" or an abbreviation. This contrast adjustment needs to be set to suit the individual LCD module.


OBLCD: Power supply

The LCD module draws its power from the SPLat board. LCD modules draw typically 2-3mA at 5V.


OBLCD: Backlighting

The LCD module supplied with the MMi99DK216 and early shipments of MMi200DK216 has no provision for backlighting. If you supply your own LCD module you may use one with backlighting.

There are 2 technologies commonly used for backlighting LCD modules: EL (Electro Luminescent) and LED (Light Emitting Diodes).

EL backlighting requires a special high voltage power supply. Please consult your supplier for details.

LED backlighting comes in two flavors. The older type uses an array of LED chips, usually green, mounted on a substrate, and is quite current-hungry. The newer type uses one single high intensity white LED in conjunction with special polarizers and other materials in the LCD proper, with a resulting blue light. Whereas the old type of display can be viewed without the backlighting turned on, the new type is unreadable without the light on.

New, blue light LED backlights using white LEDs

The LCDs used in the MS12, MS120, in later MMi200DK216's and in MMi201cDK216, use the newer type of backlight. The current consumption of the backlight is about 20mA. On the MS120 the backlight is controlled by output 20, and defaults off. In the MS120 with recent Firmware it is also dimmable. On the MMi200DK216 and MMi201cDK216 the backlight (if fitted) is permanently on. On MMi202s recent firmware allows dimming of the backlight providing a special dimmable LCD is fitted.

The white LED technology used for the blue backlight has a lifetime of approximately 3000 hours. To extend the your product lifetime on an MS120 you should therefore program an automatic turn-off or dim when the LCD is not in use. Example code. For MMi202 you can select whether to use a backlit or non-backlit display. If you have a display with dimmable backlight you can program it to dim down. Dimming or turning off the backlight will extend its life. For programming examples see Example LCD backlight auto-off timers. Dimmable displays for the MMi202 are available from us.

Old, LED array backlights

The older LED-array type of backlighting requires a comparatively high current (100-400mA, depending on the module) at a fairly low voltage. This can be quite difficult to supply. LEDs require a controlled current, not a controlled voltage. That means there must always be a current controlling device between the supply voltage and the LED. Usually this will be a simple resistor, but the selection of the resistor is critical. Do the wrong thing and you will blow up the display instantly. We have never offered this type of backlight on standard products like the MMi because there is no safe, cost effective and practical way of handling a wide range of supply voltages with such a backlight.

Under no circumstances connect an LED backlight directly across the supply voltage, no matter what the voltage is.

Here is an example calculation:

The manufacturer's data sheet for the LED backlighting specifies a typical operating current of 125mA, with a voltage drop across the LEDs of typically 4.1V. The supply voltage to the SPLat board is 24VDC. Calculate the required series resistance.

The LEDs take about 4.1V. That will leave 19.9V across the resistor. From Ohm's law the resistor needs to be 19.9V/125mA=160Ohms. The nearest commonly available standard value is 150Ohms.

The heat dissipated in that resistor will be 19.9Vx19.9V/150Ohms=2.64Watts. That means you need to use at least a 5W resistor, and it's going to get hot.


OBLCD: Special characters

The OBLCD can display the following non-ASCII characters if supplied the byte code in column 1. This is supported by the OBLCD_Text instruction.

c | Character | Description/clarification
34 | " | Quotation mark
126 | –> | Right arrow
127 | <– | Left arrow
223 | ° | Degree sign
224 |  | small alpha
225 | ä | a-umlaut
226 | ß | beta
228 | µ | mu
229 |  | sigma
230 |  | rho
232 |  | square root
236 | ¢ | cent
239 | ö | o-umlaut
244 |  | omega
245 | ü | u-umlaut
246 |  | Sigma
247 |  | pi
248 |  | x-bar
253 | ÷ | divide sign

For technical reasons we've not been able to show all characters in the second column.

Note: OBLCD_SpclChar was used prior to SPLat/PC V7.15.1 to display special characters. That functionality has now been moved to OBLCD_Text. OBLCD_SpclChar is deprecated and should not be used for new programs.


OBLCD: Programming

Programming of the LCD is quite easy. In this chapter we take you through the whole repertoire of LCD programming instructions. The material is organized in broad functional areas. There are several substantial examples at the end of the chapter.

Before you jump in and start writing a program for the LCD, you should carefully work out what you want to happen. As with all programming, a small amount of time spent planning can save hours of frustration later.

The onboard LCD is a SPLat SPx peripheral that is implemented as part of a SPLat controller board, for example the MMi201. Although the LCD module may actually be separated from the SPLat board by a cable, it is logically considered to be onboard because it is controlled by the same microprocessor chip as the controller board.

All instructions for the onboard LCD start with "OBLCD_", for example OBLCD_CurBlink. These instructions are all synthesized during program translation from lower level SPx instructions. This is a process you need never concern yourself with, except that some of the instructions alter the U and V registers. If you are using no other SPx or SPiceX peripherals, that will not cause any complications.

Previous Topic

Next Topic

In This Chapter

OBLCD: Initializing the LCD

OBLCD: Programming the cursor, clearing the screen

OBLCD: Programming text messages

OBLCD: Programming numeric output

OBLCD: Programming bar graphs

OBLCD: Programming housekeeping functions

OBLCD: Timing considerations

OBLCD: Examples


OBLCD: Initializing the LCD

Before you can use the LCD it must be initialized. This simply tells the SPLat controller what size of LCD it is driving.

On the MS12 (and possibly future products) you must also turn on the backlight. For the MS12 the backlight is controlled by output 20. The MS120 offers the same simple on/off control as well as dimming of the backlight. MMi202s shipped after May 2009 also provide backlight dimming. See Example: LCD backlight auto-off timers for more information.

See also Contrast adjustment.

In This Section

OBLCD_Type tt


OBLCD_Type tt

This instruction tells the SPLat controller what type of LCD it is controlling. It also tells the control chip within the LCD module itself. Without this the system cannot correctly manipulate the cursor, allow for the correct line lengths etc.

The instruction argument tt defines the LCD type according to the following table:

tt | Rows | Columns | Comment
1 | 1 | 16 |
2 | 2 | 16 | Default type
3 | 4 | 16 |
4 | 2 | 20 |
5 | 4 | 20 |

This instruction will destroy any dimming settings made using OBLCD_Dim.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD: Programming the cursor, clearing the screen

The cursor position is the place on the LCD screen where any new information will be displayed. It may be visible or invisible. When it is visible it may may be a simple underline or a blinking rectangular block.

You need to take great care to make sure the cursor is where you want it before you actually display something on the LCD. When you do display something, be it a text message, a number or a special character, the cursor will end up at the character position following the last character displayed. That means you can display 2 or more items, one after the other, and they will be displayed in the same order.

The cursor behaves "correctly" at the end of a display line, i.e. carries down to the start of the following line (providing you have initialized to the correct display type). However, when it gets to the very end of the display its behaviour is unpredictable.

The LCD has no scroll function.

In This Section

OBLCD_SetCur r,c

OBLCD_CurOn

OBLCD_CurOff

OBLCD_CurBlink

OBLCD_Cls

OBLCD_GetCurYX [D>=16]

OBLCD_SetCurYX [D>=16]


OBLCD_SetCur r,c

This instruction moves the cursor to the specified row, r, and column, c. It does not affect whether the cursor is visible or not.

Rows are counted up from 0, starting at the top. Columns are counted up from 0, starting at the far left. Hence, valid values for the default LCD type, a 2x16 display, are r=0 or r=1, and c=0...15.

If you specify invalid values for row and column, the display will become unpredictable. There is no error checking. The SPLat/PC simulator will not behave like the actual controller under abnormal use.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_CurOn

This instruction turns on the LCD cursor, i.e. makes it visible. The cursor appears as an underline.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_CurOff

This instruction turns off the LCD cursor, i.e. makes it invisible.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_CurBlink

Makes the cursor appear as a blinking rectangular block.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_Cls

Clears the LCD screen and positions the cursor at row 0, column 0.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_GetCurYX [D>=16]

This instruction the current OBLCD cursor position onto the register stack, with Y becoming the row number and X becoming the column number. This can be used to save the current cursor.

Rows are counted up from 0, starting at the top. Columns are counted up from 0, starting at the far left.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 16. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).

See also OBLCD_SetCurYX OBLCD_SetCur


OBLCD_SetCurYX [D>=16]

This instruction moves the cursor to the row number in the Y register and the column number in the X register. It does not affect whether the cursor is visible or not.

Rows are counted up from 0, starting at the top. Columns are counted up from 0, starting at the far left.

If you specify invalid values for row and column, the display will become unpredictable. There is no error checking. The SPLat/PC simulator will not behave like the actual controller under abnormal use.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 16. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and firmware version).

See also OBLCD_GetCurYX OBLCD_SetCur


OBLCD: Programming text messages

Text messages are very easy to generate. They get displayed at the current cursor position. You should plan your text messages carefully.

There are a number of special characters that can be displayed. These are characters that do not appear on your computer keyboard. The double-quote, ", is also treated as a special character because it has a special meaning in a SPLat program. Special characters are displayed using a separate instruction.

In This Section

OBLCD_CharX [D>=

OBLCD_Text "Message"

OBLCD_NVText nn [D>=15]

OBLCD_SpclChar c


OBLCD_CharX [D>=

Pops X and sends the character from X to the LCD at the current cursor location. This is useful for displaying individual characters and for applications that receive ASCII input from the serial port. Destroys UV.

Note: This instruction was introduced simultaneously with dialect 25, but will work on boards back to dialect 9 (the oldest boards supporting the OBLCD)


OBLCD_Text "Message"

This instruction displays the specified byte string on the LCD starting at the current cursor position.

The string argument may consist of a series of mixed strings (quoted) and byte constants separated by commas. For example:

       OBLCD_Text     223,"C"

will display a degree sign followed by a letter C. 223 is the special character code for the degree sign.

Click here for special character codes.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).

The text to be displayed must be enclosed in quotation marks. The quotation marks are not displayed on the LCD. You cannot include quotation marks in a text message. Instead use special character codes. Example:

       OBLCD_Text    "Jack said ",34,"Bye",34," and left."

34 is the special character code for a quotation mark.


OBLCD_NVText nn [D>=15]

This instruction combines the onboard LCD with NVEM0. It retrieves a text string from an NVEM0 table and displays it on the LCD at the current cursor position.

The text string is retrieved from the currently selected record, and NVEM0 address Pointer + RecLen x RecNum + nn. The number of bytes fetched and displayed is controlled by the NVEM record length register.

Note: This instruction modifies UV

Example

Please refer to the NVEM0 documentation for more details on the use of NVEM0 and to OBLCD documentation for details on the LCD.

Hint: The byte string fed to the OBLCD from NVEM0 may contain special characters.

Dialect exclusions: Not available in dialects before 14. Also not available in boards that do not have OBLCD support.


OBLCD_SpclChar c

This instruction allows you to display characters that are otherwise inaccessible, for example a degree sign. The instruction argument c determines what gets displayed.

Click here for special character codes.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).

This instruction is deprecated and should not be used for new programs. The handling of the OBLCD_Text instruction in SPLat/PC has been enhanced to replace it.


OBLCD: Programming numeric output

There are a number of instructions available for displaying numbers. These cover several possibilities for:

  • Displaying memory directly;
  • Displaying the value in X;
  • Displaying floating point numbers in W.

In This Section

OBLCD_UDecDispMVW mm

OBLCD_UDecDispMFW mm

OBLCD_SDecDispMVW mm

OBLCD_SDecDispMFW mm

OBLCD_UDecDispXVW

OBLCD_UDecDispXFW

OBLCD_SDecDispXVW

OBLCD_SDecDispXFW

OBLCD_HexDispX

OBLCD_fDispW f,p


OBLCD_UDecDispMVW mm

Displays the contents of RAM location m as an unsigned decimal number in a variable width field. As the capacity a single RAM location is a number between 0 and 255, the resulting display can be 1, 2 or 3 characters long.

Examples:

1

12

123

Note: This instruction modifies UV. Consumes one level of the register stack, i.e. loses T

Note: This instruction does not jndex its argument, nor does it respond to an IasJ: precode.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and firmware version).


OBLCD_UDecDispMFW mm

Displays the contents of RAM location m as an unsigned decimal number right justified in a fixed width field of 3 character positions. Leading zeroes are not displayed.

Examples (spaces are shown as ^):

^^1

^12

123

Note: This instruction modifies UV. Consumes one level of the register stack, i.e. loses T

Note: This instruction does not jndex its argument, nor does it respond to an IasJ: precode.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_SDecDispMVW mm

Displays the contents of RAM location m as a signed decimal number in a variable width field. The resulting display can be 1, 2, 3 or 4 characters long.

Examples:

1

12

-123

Because SPLat is not intended for doing arithmetic manipulations in the register stack, you would seldom interpret values in X as negative (hence you are unlikely to ever use this instruction). If you are doing arithmetic operations you are better off using the floating point processor.

If you do decide to do some signed integer counting, you will need to understand the principles of 2's complement binary number notation. In 2's complement the capacity of X is -128 to +127.

Note: This instruction modifies UV and consumes one level of the register stack, i.e. loses T

Note: This instruction does not jndex its argument, nor does it respond to an IasJ: precode.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_SDecDispMFW mm

Displays the contents of RAM location mm as a signed decimal number right justified in a fixed width field 4 characters long.

Examples:

^^^1

^^12

-123

Note: This instruction modifies UV and consumes one level of the register stack, i.e. loses T

Note: This instruction does not jndex its argument, nor does it respond to an IasJ: precode.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_UDecDispXVW

Displays the contents of X as an unsigned decimal number in a variable width field. As the capacity of X is a number between 0 and 255, the resulting display can be 1, 2 or 3 characters long.

Examples:

1

12

123

Note: This instruction modifies UV. Pops X

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and firmware version).


OBLCD_UDecDispXFW

Displays the contents of X as an unsigned decimal number right justified in a fixed width field of 3 character positions. Leading zeroes are not displayed.

Examples (spaces are shown as ^):

^^1

^12

123

Note: This instruction modifies UV. Pops X

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and firmware version).


OBLCD_SDecDispXVW

Displays the contents of X as a signed decimal number in a variable width field. The resulting display can be 1, 2, 3 or 4 characters long.

Examples:

1

12

-123

Note: This instruction modifies UV. Pops X

Because SPLat is not intended for doing arithmetic manipulations in the register stack, you would seldom interpret values in X as negative (hence you are unlikely to ever use this instruction). If you are doing arithmetic operations you are better off using the floating point processor.

If you do decide to do some signed integer counting, you will need to understand the principles of 2's complement binary number notation. In 2's complement the capacity of X is -128 to +127.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and firmware version).


OBLCD_SDecDispXFW

Displays the contents of X as a signed decimal number right justified in a fixed width field 4 characters long.

Examples:

^^^1

^^12

-123

Note: This instruction modifies UV. Pops X

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_HexDispX

Displays the contents of X as a hexadecimal number in a 2-character field

Note: This instruction modifies UV and POPs X

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD_fDispW f,p

Displays the floating point number in W according to the format specifiers f and p.

f specifies the number of character positions that is to be used. f must be between 1 and 9. Remember that the precision of the floating point numbers in SPLat is about 7 decimal digits. The number will be displayed right-justified in the field width you specify in f. Unused character positions will contain a space.

p specifies the maximum number of digits to be displayed after the decimal point.

The number is rounded in accordance with the number of digits displayed.

Note: This instruction modifies UV

Examples (spaces shown as ^):

W | f | p | Display | Comments
1.23456 | 5 | 3 | 1.235 | Note rounding
-1.23456 | 5 | 3 | -1.23 | Limited by f character positions
-1.23456 | 9 | 3 | ^^^-1.235 | Limited by 3 decimal places
12.3456 | 5 | 3 | 12.35 |
123.456 | 5 | 3 | 123.4 |
1234.56 | 5 | 3 | ^1235 | Decimal point suppressed
12345.6 | 5 | 3 | 12346 |
123456 | 5 | 3 | +++++ | Overflow: result can't fit in field
-12345.6 | 5 | 3 | ----- | Underflow (negative overflow)
0.123456 | 5 | 3 | 0.123 | Leading "0." is displayed
-0.123456 | 5 | 3 | -0.12 |
-0.00012345 | 5 | 3 | -0.000 | Take care with small numbers!

In SPLat applications you are unlikely to need to display very large or very small (close to 0) numbers. The numbers you display will usually be count values or some engineering units. You will presumably have a good idea of the expected range of values before you start, and will scale appropriately. For example, if your program totalizes the amount of cement packed you would scale your estimate to tons, not grams.

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD: Programming bar graphs

In controllers with dialect 18 or later, the LCD can display simple horizontal bar graphs. The graphs are generated using 5 special "chunky graphics" characters with respectively 1 through 5 vertical columns of dots. These characters are accessed either via special character codes, or more conveniently through the OBLCD_HGraph instruction.

In This Section

OBLCD_HGraph [D>=18]


OBLCD_HGraph [D>=18]

Displays the contents of X as a horizontal bar graph using graphical "block" characters, with a full scale value of Y. X and Y are expressed as pixels of graph width. Example

Note: This instruction modifies UV. Pops X and Y

Dialect exclusions: Not available in dialects before 18. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD: Programming housekeeping functions

These instructions fall under the "advanced topic" heading. The LCD driver automatically waits for the LCD if it busy. An advanced programmer could use these instructions to avoid the processor being blocked by the LCD: test the LCD, and if it's busy go away and do something else, then come back later.

Note: From dialect 16 the OBLCD is fully buffered, so there is no longer any need to worry about it blocking the processor.

In This Section

OBLCD_GoIfBusy line

OBLCD_GoIfIdle line

OBLCD_RUT


OBLCD_GoIfBusy line

If the LCD is busy, go to the nominated line, otherwise drop through to the next instruction. See also Timing considerations.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).

From dialect 16 this instruction is not needed because the OBLCD is fully buffered and will never block your program.


OBLCD_GoIfIdle line

If the LCD is idle (not busy), go to the nominated line, otherwise drop through to the next instruction. See also Timing considerations.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).

From dialect 16 this instruction is not needed because the OBLCD is fully buffered and will never block your program.


OBLCD_RUT

This instruction tests for the presence of the onboard LCD port ("RUT" = "aRe U There?", a hangover from old telex/twx terminology). Naturally, any board that possesses an LCD port will report it as being there. The result is pushed into X as a True. If the board does not have an LCD port this instruction will result in a fatal runtime error, and your program will re-start.

In general all SPx peripherals must respond to an RUT poll. With external peripherals the usefulness of an RUT is to allow a system to determine its own configuration. With onboard peripherals it is included for consistency but is of little practical use.

This instruction cannot detect the absence or presence of a LCD module plugged into the LCD port. It only tells you that the board has an LCD port.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


OBLCD: Timing considerations

In SPLat MMi201 controllers with dialect 16 and later, the OBLCD is fully buffered. That means that instruction that display to the LCD actually write to a buffer memory in the processor. This happens very fast. After that the processor updates the LCD in the background. That takes longer but does not block your program. It is still advisable to not do LCD display instructions too often, as they still place an additional burden on the processor, especially numeric display instructions and very especially the OBLCD_fDispW instruction.

The following applies to all boards with dialects before 16 and to all versions of the MS12:

Transferring data and commands to the LCD module takes time.

The first instruction you send to the LCD will have no significant timing impact on your program. It simply gets passed to the LCD handler on the SPLat board, and from then on it is quietly sent to the LCD in the background. However, if your program tries to send a second instruction before the first one has been sent to the LCD, it will get held up, and will prevent the SPLat processor from doing anything else, until the first one is fully sent. Each subsequent instruction must wait for the previous one to be completed (this happens automatically).

There are several ways to manage this:

  1. Avoid redundant LCD updates. For example update readings once per second rather than 10 times per second. You can also skip the update if the value hasn't changed. Update the changing number but not the fixed text.
  2. Update the LCD only when your program is not doing something very time critical.
  3. Study the WaitForLCD, OBLCD_GoIfBusy and OBLCD_GoIfIdle instructions so you only send stuff to the LCD when it is known to be idle.

Similarly, the OBLCD_fDispW instruction requires a lot of calculation, so if you do it too frequently your program will slow down significantly.

Custom controllers with OBLCD

Please consult SPLat Controls to establish if your controller has a fully buffered display driver.


OBLCD: Examples

The mechanics of programming the LCD are quite simple. There are 4 basic steps:

  1. initialize the LCD
  2. Position the cursor
  3. Display text
  4. Display numbers

The key to success is careful planning; planning of exactly what you will display and planning of exactly where it will be displayed and how much space it will take.

One useful planning tool is to type up intended displays on your computer using a mono-spaced font like Courier New or Fixed Sys, for example:

0123456789012345 (ruler line)
Temp = 34.5°C OK
Press=12.3kPa Hi

Here the top line is used as a ruler, showing column numbers for a 16 character line. You can see I had to sacrifice the spaces before and after the equals sign in the pressure display, in order to fit in the status indicator.

The following pages provide some worked code examples for various situations.

In This Section

Example: Simple text display with OBLCD

Example: Integer count display with OBLCD

Example: Temperature readout with limits with OBLCD

Example: A simple menu system with OBLCD

Example: Bargraph on OBLCD

Example: LCD backlight auto-off timers


Example: Simple text display with OBLCD

This example displays a variable status message for some sort of washing machine (maybe an industrial parts washer). The machine can have the following states: Idle; filling; heating; washing; emptying; rinsing and cooling.

The status display "owns" the bottom line of a 2x16 LCD, i.e. row 1. We will write the display function as a self-contained subroutine. It will be called with a number in X that indicates the current machine status, i.e. dictates which message is to be displayed. The following table defines call arguments (X values) versus action or display message:

X | Status | Comment
0 | Initialize | Called only at startup to initialize LCD
1 | Idle |
2 | Filling |
3 | Heating |
4 | Washing |
5 | Emptying |
6 | Rinsing |
7 | Cooling |

X=0 is special in that it initializes the LCD. It should be called only once, as it will result in the screen being erased, which could interfere with the "owners" of other lines of the LCD.

We need to plan exactly what we intend to display for each state, so we can be sure our messages will fit.

0123456789012345 (ruler line)
Status: Idle
Status: Filling
Status: Heating
Status: Washing
Status: Emptying
Status: Rinsing
Status: Cooling

As luck would have it, the longest status word "Emptying" can fit together with "Status: ". Had that not been the case we would have had to think up some different words, or abbreviate or eliminate "Status: ".

One thing we must watch, though, is that some words are shorter, so we will make them all the same length by padding with spaces. That avoids one word being displayed with the remnants of a previous, longer word also showing, for example "Idling" or "Rinsing".

We will also need a small amount of code to test the message display. To do this we use two front panel buttons on the MMi201 to count up and count down a memory variable "Status", making sure to not go over 7 or under 0.

;Status message display example from onboard LCD documentation

;Start of test code vvvvvvvvvvvvvvvvvvvv

iUpKey     iEQU     12

iDnKey     iEQU     11

Status     mEQU     0

Start

          GoSub     StatusDisplay  ;initialize

          SetMem    Status,1

Loop

          InputK    iUpKey

          GoIfT     Up

          InputK    iDnKey

          GoIfT     Down

          GoTo      Loop

Up

          Recall    Status

          Push

          GoIfXGE   7,Loop

          IncX

          Push

          Store     Status

          GoSub     StatusDisplay

          GoTo      Loop

Down

          Recall    Status

          Push

          GoIfXLE   1,Loop

          DecX

          Push

          Store     Status

          GoSub     StatusDisplay

          GoTo      Loop

;End of test code ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

StatusDisplay:

      Branch                       ;Test X

      Target          Status0      ;Goes here if X=0

      Target          Status1      ;Goes here if X=1

      Target          Status2      ;Goes here if X=2

      Target          Status3      ;Goes here if X=3

      Target          Status4      ;Goes here if X=4

      Target          Status5      ;Goes here if X=5

      Target          Status6      ;Goes here if X=6

      Target          Status7      ;Goes here if X=7

Status0      ;Come here to initialize the LCD. Also, display "Status: "

      OBLCD_Type      2            ;initialize the LCD

      OBLCD_SetCur    1,0

;                     ;01234567

      OBLCD_Text      "Status: "

;"Fall through" and add the word "Idle" at initialization time.

Status1      ;Come here to display Idle message

      OBLCD_SetCur    1,8

;                     ;89012345

      OBLCD_Text      "Idle      "

      Return

Status2      ;Come here to display Fill message

      OBLCD_SetCur    1,8

;                     ;89012345

      OBLCD_Text      "Filling "

      Return

Status3      ;Come here to display Heat message

      OBLCD_SetCur    1,8

;                     ;89012345

      OBLCD_Text      "Heating "

      Return

Status4      ;Come here to display Wash message

      OBLCD_SetCur    1,8

;                     ;89012345

      OBLCD_Text      "Washing "

      Return

Status5      ;Come here to display Empty message

      OBLCD_SetCur    1,8

;                     ;89012345

      OBLCD_Text      "Emptying"

      Return

Status6      ;Come here to display Rinse message

      OBLCD_SetCur    1,8

;                     ;89012345

      OBLCD_Text      "Rinsing "

      Return

Status7      ;Come here to display Cool message

      OBLCD_SetCur    1,8

;                     ;89012345

      OBLCD_Text      "Cooling "

      Return


Example: Integer count display with OBLCD

This example gives you the chance to explore the differences between signed and unsigned integer numbers, and between fixed field width and variable field width integer formats.

We will make a program that uses the MMi99 front panel buttons to increment a number by 1 or by 10. The buttons will be assigned, from top to bottom, as "+10", "+1", 0 (clear the count), "-1" and "-10". The resulting number will be displayed in all 4 integer formats. The screen layout will be:

0123456789012345 (ruler line)

UFW:999;SFW:-999

UVW:999;SVW:-999

For example, with the number 23 the display should be:

0123456789012345 (ruler line)

UFW: 23;SFW:  23

UVW:23 ;SVW:23

In all cases the actual display field is bounded by punctuation marks or the end of the line. If you are using an LCD with more than 16 columns, take it as an exercise to modify the program to add a semicolon after the second number on each line.

Here is the program:

;Integer count on LCD. Example from onboard LCD documentation

iPlus10     iEQU  12

iPlus1      iEQU  11

iZero       iEQU  10

iMinus1     iEQU  9

iMinus10    iEQU  8

Counter       mEQU  0

     OBLCD_Type   2        ;initialize the LCD

Loop

     InputK       iPlus10  ;Test a button

     GoIfT        Add10

     InputK       iPlus1   ;Test a button

     GoIfT        Add1

     InputK       iZero    ;Test a button

     GoIfT        Clear

     InputK       iMinus10  ;Test a button

     GoIfT        Sub10

     InputK       iMinus1   ;Test a button

     GoIfT        Sub1

     GoTo         Loop

Add10

     Recall       Counter

     IncX

     IncX

     IncX

     IncX

     IncX

     IncX

     IncX

     IncX

     IncX

     IncX

     Store        Counter

     GoTo         Display

Add1

     Recall       Counter

     IncX

     Store        Counter

     GoTo         Display

Sub10

     Recall       Counter

     DecX

     DecX

     DecX

     DecX

     DecX

     DecX

     DecX

     DecX

     DecX

     DecX

     Store        Counter

     GoTo         Display

Sub1

     Recall       Counter

     DecX

     Store        Counter

     GoTo         Display

Clear

     SetMem       Counter,0

;Fall through

Display

     OBLCD_SetCur 0,0

;                 "0123456789012345"

     OBLCD_Text   "UFW:   ;SFW:    "

     OBLCD_SetCur 1,0

;                 "0123456789012345"

     OBLCD_Text   "UVW:   ;SVW:    "

     OBLCD_SetCur 0,4

     Recall       Counter

     OBLCD_UDecDispXFW

     OBLCD_SetCur 0,12

     Recall       Counter

     OBLCD_SDecDispXFW

     OBLCD_SetCur 1,4

     Recall       Counter

     OBLCD_UDecDispXVW

     OBLCD_SetCur 1,12

     Recall       Counter

     OBLCD_SDecDispXVW

     GoTo         Loop


Example: Temperature readout with limits with OBLCD

In this example we will do a complete sample application, a fermentation vessel temperature monitor with high and low limits.

The temperature sensor is a 10K thermistor probe connected to the MMi99 analog input c configured for temperature measurement (thermistor temperature measurement with SPLat is documented elsewhere). The thermistor drive voltage is generated by analog output i.

Using an Excel spreadsheet we have determined the following parameters:

Operating temperature range: 25°C - 45°C

Measurement resolution: approx 0.2°C

Optimum analog out count to thermistor driver voltage generator: 125 (=9.80V)

Equation for thermistor linearization:

T = -1.617807E-06x^3 + 1.275737E-03x^2 - 5.141065E-01x + 9.905215E+01

where x = raw analog input reading, T = temperature in °C and the ^ operator means "raised to the power of"

The optimum fermentation temperature is 35°C. Above 37°C we must display an over temperature warning and below 32°C we must display an under temperature warning. We will do an update every 100mS, using timer 4.

Display layout plan:

0123456789012345
Vat temp: 33.3°C
     Normal
   Under temp!
   OVER TEMP!!

Our program must:

  1. Initialize the thermistor circuit by setting analog output i
  2. Initialize the LCD
  3. Wait for a 100mS time tick
  4. Take an analog reading and Convert to °C
  5. Display temperature
  6. Evaluate temperature and show status
  7. Go to 3

Here's the program

;Temperature monitor program with hi/lo limits
Temperature        mEQU      0      ;4 bytes for temperature value

      LoadX        125

      AnOutI                 ;Init thermistor circuit

      OBLCD_Type   2         ;Init LCD

;                   0123456789012345

      OBLCD_Text   "Temperature"  ;"splash" screen

      OBLCD_SetCur 1,0

      OBLCD_Text   "Monitor V1.0"

      Pause        500       ;Delay 5s to let thermistor drive settle

MainLoop

      Test         4

      GoIfT        MainLoop  ;Wait for time tick

      SetTimer     4,10      ;Restart the timer

      GoSub        ReadTemp  ;Get and update temperature in memory

      GoSub        DispTemp  ;Display temperature

      GoSub        Evaluate  ;Check limits and display

      GoTo         MainLoop

;Subroutine to evaluate the temperature reading

;and display a message. Because the fCompareR instruction

;can yield a Result code in the R-register for indeterminate

;results, I've included a corresponding Branch address

;"OhDear". It does nothing.

Evaluate

      OBLCD_SetCur   1,0     ;Get that done first

      fRecallW       Temperature

      fLoadQ         37      ;Upper limit

      fCompareR

      BranchR

      Target         TempOK   ; W=Q

      Target         TempHot  ; W>Q

      Target         TestCold ; W<Q

      Target         OhDear   ; ????

TestCold

      fLoadQ         32       ;Low limit

      fCompareR

      BranchR

      Target         TempOK   ; W=Q

      Target         TempOK   ; W>Q

      Target         TempCold ; W<Q

      Target         OhDear   ; ????

TempCold

                     ;0123456789012345

      OBLCD_Text     "   Under temp!  "

      Return

TempHot

                     ;0123456789012345

      OBLCD_Text     "   OVER TEMP!!  "

      Return

TempOK

                     ;0123456789012345

      OBLCD_Text     "     Normal     "

OhDear

      Return

;Subroutine to display temperature

DispTemp

      OBLCD_SetCur   0,0

      OBLCD_Text     "Vat temp: "

      fRecallW       Temperature

      OBLCD_fDispW   4,1

      OBLCD_Text     223,"C"      ;223 = degree sign

      Return

;Subroutine to take one thermistor reading and convert to °C

;in floating point memory location Temperature.

ReadTemp

      AnInC                          ;Read thermistor

      Float

      fStore       Temperature

      fLoadQ       -1.617807E-06      ;<< X^3 coefficient

      fMul

      fLoadQ       +1.275737E-03      ;<< X^2 coefficient

      fAdd

      fRecallQ     Temperature

      fMul

      fLoadQ       -5.141065E-01     ;<< X^1 coefficient

      fAdd

      fRecallQ     Temperature

      fMul

      fLoadQ       +9.905215E+01      ;<< X^0 coefficient

      fAdd

      fStore       Temperature

      Return


Example: A simple menu system with OBLCD

The purpose of a menu system is to allow a user to move through a range of alternatives and select one, in order to either change an operating parameter or initiate an action. Most people nowadays are familiar with the menu systems in cellular telephones. A menu system you might design for a SPLat with LCD is similar.

The following example will assume that there are just 4 push buttons available. From top to bottom these are labeled:

Label | Name/function
<- | iLeftArrow
-> | iRightArrow
Y | iAccept
N | iReject

Essentially the arrow keys will be used to select and change choices, while accept and reject keys will help finalise decisions.

The menu system will affect three memory variables:

WashTime. Duration of wash cycle, in minutes

PreWash. True or False, depending if a pre wash cycle is required

HotWash. If true, do a hot wash cycle, otherwise do a cold cycle.

It is absolutely essential that you plan your menu system on paper before you start writing any program code. The following table is a plan for the menu for a washing machine. It gives a choice of hot or cold wash and allows setting of the wash time between 5 and 25 minutes. Each row of the table corresponds to one display on the LCD. We will start by analyzing this tabulated plan, then we will see how it translates into SPLat code.

 | Display | <- | -> | Y | N
0 | 0123456789012345; Y for menu |  |  | [1] |
1 | 0123456789012345; Wash type: XXXX; Change? | [3] | [2] | Hot:[5] Cold:[4] | [0]
2 | 0123456789012345; Wash time: xx; Change? | [1] | [3] | [6] | [0]
3 | 0123456789012345; Pre-wash: Xxx; Y or N to change | [2] | [1] | Xxx=Yes Save | Xxx=No Save
4 | 0123456789012345; Hot wash | [5] | [5] | Save [2] | [2]
5 | 0123456789012345; Cold wash | [4] | [4] | Save [2] | [2]
6 | 0123456789012345; Change wash time xx | Dec xx min 5 | Inc xx max 25 | Save [3] | [3]

Each row of the table is numbered. We call the numbers "State Numbers" and the whole table we call a "State Transition Table" or just "State Table". If you are familiar with SPLatMaps or bubble diagrams you may recognize this as an alternative way of representing a Finite State Machine.

The State table has 6 columns. One is just for the state number. The second column is for the LCD display in that state. The final 4 columns specify what happens if the user presses each of the 4 available buttons. These are the "action columns".

In an action column a number in [square brackets] means go to that new state (row) and change the display accordingly. The action columns can contain other actions as well, such as "Save" or change a variable (I've used X's as placeholders for variables both in the display column and in the action columns). I've also included a ruler line in the displays.

Let's go through the 6 states, one at a time:

State 0

This allows for the possibility of the LCD not always being "owned" by the menu system. Perhaps in this state the bottom line shows wash temperature. Pressing Y will take us into the menu system proper at state 1.

State 1

Offers us the option to change the wash type. Pressing Y to accept the offer takes us to state 4 or 5, depending on the current wash type selection (hot or cold). Pressing an arrow key takes us to 2 or 3. If you study the arrow key actions for state 1, 2 and 3 you will discover that they form a loop, allowing you to scroll through these 3 "top level" menu choices. Pressing the N button takes us back to state 0, or notionally out of menu mode.

State 2

Displays the current wash time and asks if we want to change it. If we accept (Y) we go to state 6, which allows the wash time to be changed.

State 3

Lets us change the selection of the pre-wash.

States 4 and 5

These work together as a pair to allow toggling between hot wash and cold wash. The arrow keys toggle between them, Y accepts the current selection by saving it and N exits back to the top level of the menu without changing the setting.

State 6

Displays the wash time and allows us to change it using the arrow keys. Y saves the changed value while N rejects it. Notice that the table specifies that the value can only be incremented and decremented between the limits 5 to 25.

A few notes on all this:

  • When we exit states 4, 5 or 6, we don't return to the state we came from but to the next state in the top level menu sequence. This is a small refinement that will assist the user by automatically moving on to the next decision that must be made.
  • State 3 updates its parameter immediately, whereas states 4/5 and 6 require the user to consciously accept or reject the change with Y or N. I've done this simply to illustrate for you the two different ways of operating. In a real world situation you should probably design for consistent behaviour.
  • State 0 is rather poorly defined. In practice it would determine how this menu system meshes with other parts of the program that may also have claim to the buttons and the LCD.
  • I called the arrow keys left and right rather than up and down, because the LCD can display left and right arrows. As it happens, I didn't actually use them in the display!

The menu system will affect three memory variables:

HotWash: True if the wash is to be hot, false if the wash is to be cold.

WashTime: A simple integer variable giving wash time in minutes ( range 5 to 25)

PreWash: True if a pre-wash cycle is required.

The code for each state follows a consistent pattern, with the following elements:

  1. On entering the state for the first time, creation of the display for that state.
  2. A loop to look for one of the allowable keys, with a jump to an action segment if a key is pressed
  3. One action segment for each key. This may be an update of a variable and/or the display, or a jump to another state.

Here we go with the program:

;Sample menu system for washing machine

;Define keys

iLeftArrow    iEQU          12      ;top button on MMi99

iRightArrow   iEQU          11

iAccept       iEQU          10

iReject       iEQU          9

;Define memory locations.

;These are in the PermStore area

HotWash       mEQU          6

WashTime      mEQU          7

PreWash       mEQU          8

;These are anywhere:

ScratchMem    mEQU          9       ;Temporary storage

;initialize: Check if Permanent memory is valid,

;            force defaults if not

          PermRecall

          GoIfZ         DataOK

          SetMem        WashTime,10    ;Default

          SetMem        PreWash,T      ;Default

          SetMem        HotWash,T      ;Default

          PermStore

          PermStore

DataOK

          OBLCD_Type    2

******** Menu system ***************************

;State 0 is easy: one message then wait for Y

SetMenu0

          OBLCD_Cls

;                        0123456789012345

          OBLCD_Text    "Y for menu"

Menu0

          GoIfInK       iAccept,SetMenu1

          GoTo          Menu0

;==============State 1========================

SetMenu1

          OBLCD_Cls

;                        0123456789012345

          OBLCD_Text    "Wash type: "

          GoSub         QryChange      ;display "Change?"

Menu1ShowType

          OBLCD_SetCur   0,11

          Recall         HotWash

          GoIfT          Menu1Hot

Menu1Cold

;                        0123456789012345

          OBLCD_Text               "Cold "

          GoTo           Menu1KeyIn

Menu1Hot

;                        0123456789012345

          OBLCD_Text               "Hot  "

;Wait for a key

Menu1KeyIn

          GoIfInK        iLeftArrow,Menu1Left

          GoIfInK        iRightArrow,Menu1Right

          GoIfInK        iAccept,Menu1Accept

          GoIfInK        iReject,Menu1Reject

          GoTo           Menu1KeyIn     ;Keep trying

Menu1Left

          GoTo           SetMenu3

Menu1Right

          GoTo           SetMenu2

Menu1Accept

          Recall         HotWash

          GoIfT          SetMenu5

          GoTo           SetMenu4

Menu1Reject

          GoTo           SetMenu0

;===========State 2========================

SetMenu2

          OBLCD_Cls

;                        0123456789012345

          OBLCD_Text    "Wash time: "

          GoSub          QryChange      ;display "Change?"

Menu2ShowTime

          OBLCD_SetCur   0,11

          Recall         WashTime

          OBLCD_UDecDispXVW

          OBLCD_Text     "m "

Menu2KeyIn

          GoIfInK        iLeftArrow,Menu2Left

          GoIfInK        iRightArrow,Menu2Right

          GoIfInK        iAccept,Menu2Accept

          GoIfInK        iReject,Menu2Reject

          GoTo           Menu2KeyIn

Menu2Left

          GoTo           SetMenu1

Menu2Right

          GoTo           SetMenu3

Menu2Accept

          GoTo           SetMenu6

Menu2Reject

          GoTo           SetMenu0

;===========State 3========================

SetMenu3

          OBLCD_Cls

;                        0123456789012345

          OBLCD_Text    "Pre-wash: "

          OBLCD_SetCur   1,0

          OBLCD_Text    "Y or N to change"

Menu3Show

          OBLCD_SetCur   0,11

          Recall         PreWash

          GoIfT          Menu3PWYes

Menu3PWNo

          OBLCD_Text     "No "

          GoTo           Menu3KeyIn

Menu3PWYes

          OBLCD_Text     "Yes"

Menu3KeyIn

          GoIfInK        iLeftArrow,Menu3Left

          GoIfInK        iRightArrow,Menu3Right

          GoIfInK        iAccept,Menu3Accept

          GoIfInK        iReject,Menu3Reject

          GoTo           Menu3KeyIn

Menu3Left

          GoTo           SetMenu2

Menu3Right

          GoTo           SetMenu1

Menu3Accept

          SetMem         PreWash,T

          PermStore

          GoTo           Menu3Show

Menu3Reject

          SetMem         PreWash,F

          PermStore

          GoTo           Menu3Show

;======= State 4 =======================

SetMenu4

          OBLCD_Cls

;                        0123456789012345

          OBLCD_Text    "Hot wash"

Menu4KeyIn

          GoIfInK        iLeftArrow,Menu4Left

          GoIfInK        iRightArrow,Menu4Right

          GoIfInK        iAccept,Menu4Accept

          GoIfInK        iReject,Menu4Reject

          GoTo           Menu4KeyIn

Menu4Left

          GoTo           SetMenu5

Menu4Right

          GoTo           SetMenu5

Menu4Accept

          SetMem         HotWash,T

          PermStore

Menu4Reject

          GoTo           SetMenu2

;======= State 5 =======================

SetMenu5

          OBLCD_Cls

;                        0123456789012345

          OBLCD_Text    "Cold wash"

Menu5KeyIn

          GoIfInK        iLeftArrow,Menu5Left

          GoIfInK        iRightArrow,Menu5Right

          GoIfInK        iAccept,Menu5Accept

          GoIfInK        iReject,Menu5Reject

          GoTo           Menu5KeyIn

Menu5Left

          GoTo           SetMenu4

Menu5Right

          GoTo           SetMenu4

Menu5Accept

          SetMem         HotWash,F

          PermStore

Menu5Reject

          GoTo           SetMenu2

;===========State 6========================

SetMenu6

          OBLCD_Cls

;                        0123456789012345

          OBLCD_Text    "Change wash time"

          Recall         WashTime

Menu6Show

          Store          ScratchMem

          OBLCD_SetCur   1,6

          Recall         ScratchMem

          OBLCD_UDecDispXVW

          OBLCD_Text     "m "

Menu6KeyIn

          GoIfInK        iLeftArrow,Menu6Left

          GoIfInK        iRightArrow,Menu6Right

          GoIfInK        iAccept,Menu6Accept

          GoIfInK        iReject,Menu6Reject

          GoTo           Menu6KeyIn

Menu6Left

          Recall         ScratchMem

          Push

          GoIfXGE        25,Menu6KeyIn

          IncX

          GoTo           Menu6Show

Menu6Right

          Recall         ScratchMem

          Push

          GoIfXLE        5,Menu6KeyIn

          DecX

          GoTo           Menu6Show

Menu6Accept

          Recall         ScratchMem

          Store          WashTime

          PermStore

Menu6Reject

          GoTo           SetMenu3

;===============================================

;Subroutine to display "Change?" (clears to EOL)

QryChange

          OBLCD_SetCur   1,0

;                         0123456789012345

          OBLCD_Text     "Change?         "

          Return

;============ End of program ================

Some notes about this code:

  • Notice the very tidy factoring into one block of code per state. Notice also that all sections are essentially identical in layout. This uniformity makes the code easier to read. It is also a result of using a lot of copy/paste operations, saving a lot of typing.
  • In each section there is the same sequence of testing each key and jumping if it has been pressed. Following that are the 4 action addresses and the code for each. In many cases the code at each action is just another jump to a new state. That means each state change uses two jumps to get there. I could have saved code and memory by jumping to the new state straight after the key test, but that would have made the program a little less regular and therefore more error prone. The only deviation from this schema is state 0, which is a little different in intent and function.
  • I used a subroutine to display "Change?", because that operation occurs twice. A very modest saving in memory, perhaps, but it usually pays to place repeated operations into subroutines.
  • I have used very few comments. This is in part to leave to you to discover how the code works. Also, it is an unfortunate fact that the code for a state machine tends to obscure the logic, so making intelligible comments can be difficult. You really need access to the underlying plan, in this case the state transition table. Of course, the table could be included as a huge comment!


Example: Bargraph on OBLCD

The following program counts a counter up and down on inputs 2 and 1 respectively, and redraws a horizontal bargraph on input 3. (Note: Requires a controller with dialect 18 or later).

;Horizontal graph example
;Input 2 increments value
;Input 3 decrements value
;Input 3 updates the display
Counter defBYTE

        OBLCD_SetCur    0,0

        OBLCD_Text      "E              F"

Wait:

        InputK          3

        GoSubIfT        Graphit

        InputK          2

        GoSubIfT        Up

        InputK          1

        GoSubIfT        Dn

        GoTo            Wait

Up:

        GoIfMGT         Counter,69,UpRet

        IncM            Counter

UpRet:  Return

Dn:

        RetIfMZ         Counter

        DecM            Counter

        Return

Graphit:

        LoadX           70      ;total scale length in pixels

        Recall          Counter

        OBLCD_SetCur    0,1

        OBLCD_HGRaph

        Return


Example: LCD backlight auto-off timers

The white LED backlight used in certain LCD displays has a lifetime of about 3000 hours at full operating current (longer at reduced currents). This is an inherent limitation of the white LED technology (2009). It is therefore advisable to turn off or dim the backlight when not in use.

This page contains several examples for either totally turning off or dimming the backlight after a certain time with no detected user activity. The light comes back on whenever a front panel button is pushed. Which of the examples you follow depends on:

  • Which controller you have, MMi202, MS120 or MS12
  • For the MMi202, whether it has a dimmable LCD backlight (provided via a hardware modification introduced May 2009)
  • Which version of Firmware is in the controller.
  • If you are using MultiTrack

Example 1: Simple on/off for MS120 and MS12 using MultiTrack

The following program code will provide a simple auto-off timer for the MS120 or MS12 backlight. It turns on the backlight whenever a front panel button is pressed, and turns it off again 10 minutes after the last button press. This is designed as a MultiTrack task. This will work in any firmware that supports MultiTrack, providing your program already uses MultiTrack. All MS120s support MultiTrack. The MS12 requires firmware V3.13 or later. For more information see reFlash: How do I determine what version firmware is in my controller?

Your program's initialisation code must contain a GoSub BKLT_Init.

BKLT_Init:      LaunchTask      BKLT_Set0
                Return

BKLT_Set0:      On              20      ;Turn On backlight

                MarkTime

BKLT_0:

                YieldTask

                InputFM         12      ;Get the front panel buttons

                LoadX           %11111  ;select the 5 bits representing buttons

                AndM

                GoIfT           BKLT_Set0       ;G/ any button pressed

                LoopIfTiming    60000,BKLT_0    ;Test for timeout, loop if not yet

BKLT_Set1:

                Off             20      ;Turn Off backlight

BKLT_1:

                YieldTask

                InputFM         12      ;Get the front panel buttons

                LoadX           %11111  ;select the 5 bits representing buttons

                AndM

                GoIfT           BKLT_Set0 ;G/ any button pressed

                GoTo            BKLT_1

Example 2: Dimmed backlight for MS120 with suitable firmware and MMi202 with suitable firmware and hardware.

If the MS120 has firmware V3.20 or later it incorporates a simple "set and forget" dimming mechanism.

If the MMi202 contains firmware V3.21 or later and it is fitted with a dimmable LCD, it supports the same "set and forget" dimming mechanism.

Your program can simply run the following code just after turn-on (i.e. in its initialization code) and the dimming will happen automatically:-

BL_HighLevel:   EQU        8    ;"Bright" level, 0 to 8, 8 = 100%

BL_LowLevel:    EQU        1    ;"Dimmed" level, 0 to 8, 1 = 12.5%

BL_DimTime:     EQU        30   ;Timer value, in minutes

                OBLCD_Type 2    ;MUST be done before the dimmer stuff!!. Not required in MS12x

                SetU       0,BL_HighLevel

                SetU       1,BL_LowLevel

                SPxCmd2    1,!cpu

                SetU       0,BL_DimTime

                SPxCmd1    4,!cpu

Note that BL_HighLevel and BL_LowLevel must be between 0 (fully off) and 8 (fully on). Any other value will generate an error. BL_DimTime can be 0 to 255. 0 will cause the backlight to dim immediately. 255 will give 4 hours and 15 minutes.

With this mechanism the front panel push buttons are monitored automatically (you don't have to do anything). Whenever a button is pressed it sets the backlight to BL_HightLevel and starts the timer. Every subsequent button press restarts the timer. If the timer expires the backlight is set to BL_LowLevel.

Example 3: Automatic detection of MS12 or MS120 and firmware version

This example lets your program automatically determine if it is running in an MS12, an MS120 with old firmware or an MS120 with newer firmware able to support backlight dimming, then initialize the display accordingly.

BL_Init:        SPxPoll2        3,!CPU       ;get board type

                pushu           1

                GoIfXLT         53,BL_Old    ;MS12 is type numbers 48-52

BL_ms120:       SpxPoll2        0,!CPU       ;Get Firmware Number

                pushu           0

                push

                goifxgt         3,BL_New     ;Firmware is V4.x (something later than this code!)

;There has never been a firmware lower than 3.x, so assume 3.x and test the release number x

                pushu           1

                goifxge         20,BL_New    ;3.20 and later support dimming

BL_Old:    initialize non-dimmable backlight

                goto            BL_Finished

BL_New:    initialize dimmable backlight

BL_Finished: ' ... continue ...


OBLCD: ModBus interaction

Standard controllers with dialect 21 or later are able to have their OBLCD information transferred to other devices via ModBus. In dialect 21 this is possible only when the SPLat controller is the ModBus slave. It is however possible with all standard controllers, CC18, MS120, MMi202 and SL100. That means that even those controllers that cannot support an OnBoard LCD device (hardware) can use OBLCD instructions to display messages on an external, 3rd party, display - providing that display can be a ModBus RTU master.

The OBLCD logical device in a SPLat controller contains an area of memory of up to 80 bytes. The size depends on the OBLCD_Type. When an external ModBus master addresses a special range of memory within the SPLat, it accesses that buffer (See ModBus: Resource mapping for actual addresses). Hence the external device can extract the LCD buffer and (presumably) display it on its own display (for example a touch screen HMI).

The data found in the OBLCD buffer is the same data that has been sent there by the SPLat program. This has consequences for special characters such as degree signs. The regular LCD modules do not use ASCII codes for such characters. This is reflected in the OBLCD: Special characters. 3rd party HMIs are more likely to use standard ASCII codes, and will give strange results with OBLCD special character codes.

For a given OBLCD_Type, the displayed characters occupy consecutive locations in the buffer. For example, for a 2x16 display (type 2), they occupy the first 32 bytes of the buffer. For a 4x20 display (type 5) they use the whole buffer. The buffer contents outside the display type defined by OBLCD_Type is unpredictable.

If you are using a SPLat Controller with an LCD, say an MS120, it can get interesting, particularly if you set the OBLCD_Type to a different type. Say you have a 2x16 display but set OBLCD_Type to 5 (4x20). The actual onboard display will show the first 16 characters of the first two lines of the display buffer. The whole display buffer will be accessible by an external HMI.


OBLCD: Support in SPLat/PC

SPLat/PC provides simulation support for the onboard LCD:

  • Supports all LCD types.
  • Display simulation with 99% accuracy.
  • Special characters are all represented except X-bar, which does not exist in standard Windows fonts.
  • The OBLCD_ instructions are all simulated except OBLCD_GoIfBusy, OBLCD_GoIfIdle, OBLCD_WaitIdle.
  • OBLCD_RUT returns true if the simulated screen is open.
  • Access to the LCD through low level UV instructions is not supported, but UV gets modified in a realistic way by the high level OBLCD_ instructions.
  • Floating point formatting will produce minor discrepancies due to rounding and algorithm differences.

Versions of SPLat/PC prior to V7.15.1 did not have OBLCD simulation.


Onboard counter type A

Previous Topic

Next Topic

In This Chapter

OBCA: Introduction

OBCA: Hardware

OBCA: Theory of operation

OBCA: Programming

OBCA: Examples


OBCA: Introduction

The OnBoard Counter type A (normally abbreviated to "OBCA") is an onboard SPx peripheral that is implemented on a number of SPLat controller boards. This guide covers the aspects of the OBCA that are common to all implementations.

The OBCA is a hardware feature of the controller's microprocessor chip made available to you as part of the SPLat programming language. Early releases of the MMi99 and SL99 did not contain the OBCA in their Firmware, but can be upgraded via the reFlash facility to incorporate the OBCA function.

You should also read the OBCA specific material in the documentation for the specific controller (board) you are using.

The OBCA provides frequency counting (counting pulses over a pre-determined time interval) and totalized counting.

See also the OnBoard Quadrature Counter for a different counting feature.

Frequency counting

The frequency counting function provided by the OBCA counts input pulses over a period of 0.1S, 1S or 10S (called the timebase) and then make that count available as a floating point number. The count range is 0 to 65,535 (a 16-bit counter).

  • If a 1S timebase is used, the count obtained will be the same as the input frequency in Hz.
  • If a 0.1S timebase is used, the count will be one tenth of the frequency, meaning you could theoretically count frequencies up to about 650kHz (in practice filtering on the hardware input will make that unattainable).

With a 10S timebase you can count lower frequencies. For example, you could measure the power line frequency to a resolution of 0.1Hz.

Totalized counting

The totalizing counter function provided by the OBCA simply counts input pulses. Your program can read out the count without resetting it, or it can read and reset the counter in a single operation. The count range is 0 to 16,777,215 (a 24-bit counter).


OBCA: Hardware

There is usually no hardware exclusively devoted to the OBCA function. Basically, the OBCA comes "free" in the design of the SPLat board. On some custom boards, however, the OBCA may be associated with special purpose input conditioning circuitry. An example of that would be a variable reluctance (often referred to a VR or reluctor) proximity pickup interface, as you might use to detect engine rpm off the flywheel of a diesel engine.

That said, the OBCA operates in conjunction with a hardware input. Usually this is simply a regular input that can be used for counting via the OBCA or as a normal input. The theoretical maximum is 650kHz, as determined by the shortest timebase and the count capacity. The actual maximum count frequency on a given SPLat board will depend on any hardware input filtering on the input in question, and will typically be 15-30kHz. Please see your product specific documentation for more information.


OBCA: Theory of operation

The OBCA consists of:

  • A timebase generator which can be set for 0.1S, 1S or 10S,
  • A 16-bit hardware counter (capacity 65,535 counts). This counts incoming pulses from a physical input.
  • An 8-bit Firmware extension to the counter, which extends the count range in totalizing mode to 16,777,215 (24 bits).
  • A result register that your program can read into floating point register W and
  • A reading available flag that your program can test.

In frequency mode, at the end of each timebase interval, the counter contents is transferred to the result register, the counter is cleared and the flag is set. When the result is read out the flag is cleared again. This allows you to make sure you only process fresh readings. If you do a readout when the flag is clear, you will simply get the previous reading.

The accuracy of the readings will depend on timebase error and quantisation error. Typically this is:

  • For controllers that use a crystal for timing: 0.1% plus/minus 1 count.
  • For controllers that do not use a crystal for timing: 2% plus/minus 1 count.

In totalizing mode the counter simply accumulates counts until reset.


OBCA: Programming

Programming of the OBCA is quite simple, and requires the following steps:

  1. initialize to the required mode (time base interval)
  2. Wait for the OBCA to accumulate a reading (you can programmatically test if a reading is available)
  3. Read out the accumulated count from the OBCA
  4. Process the count, for example scale to rmp, wind speed or whatever.

The OBCA contains a timebase generator which can be set for 0.1S, 1S or 10S, a counter, a result register and a reading available flag. At the end of each timebase interval, the counter contents is transferred to the result register, the counter is cleared and the flag is set. When the result is read out the flag is cleared again. That allows you to make sure you only process fresh readings. If you do a readout when the flag is clear, you will simply get the previous reading.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

Previous Topic

Next Topic

In This Chapter

OBCA: Initialization

OBCA: Testing for readings

OBCA: Getting readings


OBCA: Initialization

In This Section

OBCA_Mode m

OBCA_Clr


OBCA_Mode m

This instruction sets the mode of subsequent OBCA operations to m.

Valid mode numbers are:

m=0 OBCA disabled [D>=9]

m=1 Measure input frequency with 0.1S timebase [D>=9]

m=2 Measure input frequency with 1S timebase [D>=9]

m=3 Measure input frequency with 10S timebase [D>=9]

m=4 totalizing counter (See note 2 below)

Note 1. This instruction modifies UV

Note 2. Mode 4 is not available in MMi99 Firmware versions before 3.22 or SL99 firmware versions before 3.41

Dialect exclusions: Not available in dialects before 9. Not available on SPLat controllers that do not have OBCA support (please check the documentation for your specific board and firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.


OBCA_Clr

Clears the counter, so it starts counting from zero. Normally used in totalizing counter mode. See also OBCA_fRdClr

Dialect exclusions: Not available in dialects before 13. Also, only available on SPLat controllers that have OBCA support with the totalizing counter feature (please check the documentation for your specific board and Firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.


OBCA: Testing for readings

There are several instructions that allow you test and respond to the reading available indicator flag. The flag is set whenever a new reading (count) becomes available. It is reset automatically whenever a reading is read out (see OBCA_fRead). If you only do a readout once each time the reading available flag gets set, you will be sure to process each reading only once.

In This Section

OBCA_GoIfRdg line

OBCA_GoIfNoRdg line

OBCA_WaitRdg

OBCA_GoSubIfRdg line

OBCA_RetIfNoRdg


OBCA_GoIfRdg line

In frequency mode only, if there is a reading available in the OBCA, go to the named line, otherwise drop through to the next instruction. This does not apply totalizing mode. See also OBCA_GoSubIfRdg, OBCA_GoIfNoRdg

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have OBCA support (please check the documentation for your specific board and Firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC


OBCA_GoIfNoRdg line

In frequency mode only, if there is no reading available in the OBCA, go to the named line, otherwise drop through to the next instruction. See also OBCA_GoIfRdg, OBCA_GoSubIfRdg

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have OBCA support (please check the documentation for your specific board and Firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC


OBCA_WaitRdg

In frequency mode only, when this instruction is executed the controller will sit and wait until a reading becomes available in the OBCA, doing nothing else while waiting. You would normally only use this instruction for very simple programs. See also OBCA_GoIfRdg, OBCA_GoSubIfRdg, OBCA_GoIfNoRdg

This is a blocking instruction. It may therefore not be used in a MultiTrack task. To get the same effect in a MultiTrack task use the sequence:

XXX:	YieldTask
        OBCA_GoIfNoRdg	XXX

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have OBCA support (please check the documentation for your specific board and Firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC


OBCA_GoSubIfRdg line

In frequency mode only, if there is a reading available in the OBCA, execute the subroutine starting at the named line, otherwise drop through to the next instruction. See also OBCA_GoIfRdg, OBCA_GoIfNoRdg

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have OBCA support (please check the documentation for your specific board and Firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC


OBCA_RetIfNoRdg

In frequency mode, if there is no reading available from the OBCA, return from the current subroutine.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have OBCA support (please check the documentation for your specific board and Firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC


OBCA: Getting readings

In This Section

OBCA_fRead

OBCA_fRdClr


OBCA_fRead

Read the OBCA count as a floating point number in W, and in frequency modes reset the reading available indicator.

In frequency modes the reading returned will be the counts accumulated in the most recent timebase period. If a count is not read out before a new count becomes available, it will be lost forever.

In totalizing mode this instruction returns the counts accumulated since the counter was last cleared with an OBCA_fRdClr or an OBCA_Clr.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have OBCA support (please check the documentation for your specific board and Firmware version).

If you are using the MMi200 please click here for information on a restriction on using this instruction.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC


OBCA_fRdClr

Read the OBCA count as a floating point number in W, and clear (reset) the counter. This instruction is intended for use in only in totalizing counter mode (Mode 4).

In totalizing mode this instruction returns the counts accumulated since the counter was last cleared with an OBCA_fRdClr or an OBCA_Clr. The main feature of this instruction is that it reads and clears simultaneously, with no missed counts.

Dialect exclusions: Not available in dialects before 12. Also, only available on SPLat controllers that have OBCA support with the totalizing counter feature (please check the documentation for your specific board and Firmware version).

OBCA_fRdClr is not available in MMi99 firmware versions before 3.22 or SL99 firmware versions before 3.41

If you are using the MMi200 please click here for information on a restriction on using this instruction.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC


OBCA: Examples

In This Section

Example: Tachometer using OBCA


Example: Tachometer using OBCA

The tachometer example tacho1.spt illustrates how the OBCA feature can be used to measure engine RPM on a diesel engine. In the example the RPM figure is displayed in 3 different ways. In a real application it might be used to maintain constant speed (with feedback to a throttle control) or to manage a startup sequence for a generator set.

Program examples are no longer installed with SPLat/PC. They are now located on our website in the File Resources area. The program file is called tacho1.spt.

The input signal for the tachometer is a proximity detector mounted close to the flywheel so the passing gear teeth produce pulses out of the detector. This is often done using a Variable Reluctance (VR) sensor, which basically consists of a coil wound on a rod magnet. The moving steel gear teeth disturb the magnetic flux, resulting in a voltage being induced in the coil. A VR sensor needs a special amplifier interface ... some custom SPLat boards contain such an interface. With a regular logic input you could use a high speed industrial proximity detector. Another method of picking up engine speed is to measure the frequency of the alternator output.

The program contains a constant for the number of gear teeth. This allows the frequency measured to be converted to RPM. If there are n teeth, there will be n pulses per revolution. Hence f, the frequency measured (as pulses per second, or Hz) will be n times the number of revolutions per second. RPM is then given as

RPM = f x 60/n

The program consists of some initialization code, a "main loop" and 4 subroutines.

The initialization code simply sets up the OBCA to mode 2, which is frequency counting with a 1S timebase.

The main loop contains nothing but a single conditional subroutine call to the tachometer subroutine Tacho. The call is conditional on there being a new reading available, using the OBCA_GoSubIfRdg instruction. By structuring the program this way with a "main loop", it is easy to add in other functions that also get called from the main loop. Providing no such function "hogs the processor" by doing a Wait or a Pause or a tight loop waiting for something to happen, the program can readily be expanded to handle a complex collection of functions.

In the Tacho subroutine we read the OBCA count and compute RPM as a simple floating point scaling calculation. The result is saved in floating point variable RPM. We then call the three display/output routines TachoToLCD, TachoToBargraph and TachoAnalogOut.

TachoToLCD takes the RPM value and displays it on the OnBoard LCD (OBLCD). You will need to Comment out the OBLCD_ instructions if you have a board that does not support the OBLCD, such as an SL99.

TachoToBargraph computes how many of the 8 LEDs (a.k.a. outputs) need to be on, then uses a Branch instruction and 8 small code segments to set the output pattern using a OutputM instructions. The LED patterns are specified in binary, e.g. %00111111. There is a test at the start of the subroutine to catch the case where the calculated number of segments exceeds 8. This is the off-scale case, and if it happens all outputs are set to blink. This test also protects us against a Branch argument exceeding the number of Targets, which would result in a runtime error.

TachoAnalogOut simply calculates an integer between 0 and 255, corresponding to 0 to 10,000 RPM, and sends it to an analog output.


OnBoard Counter type B

Previous Topic

Next Topic

In This Chapter

OBCB Introduction

OBCB Hardware

OBCB Theory of operation

OBCB Programming

Examples: OBCB


OBCB Introduction

The OnBoard Counter B (normally abbreviated to "OBCB") is an intrinsic Firmware function that is implemented on a number of SPLat controller boards. This guide covers the aspects of the OBCB that are common to all implementations.

The OBCB is a feature of the controller's operating firmware introduced in dialect 19.

You should also read the OBCB specific material in the documentation for the specific controller (board) you are using.

The OBCB provides medium speed counting on up to 8 input pins. It can be used for event counting (e.g. counting bottles on a conveyor) or for frequency measurement (e.g. engine rpm). The maximum count frequency depends on the individual controller, but is typically 1.8kHz.

See also the OnBoard Counter A for a higher speed counting feature and OnBoard Quadrature Counter for direction sensitive counting.


OBCB Hardware

There is no hardware exclusively devoted to the OBCB function. Basically, the OBCB comes "free" in the design of the SPLat board. On some custom boards, however, the OBCB may be associated with special purpose input conditioning circuitry. An example of that would be inputs designed to work off TTL levels rather than NPN sensors.

The idealized maximum count rate in current (March '07) controllers is 2kHz.The actual maximum frequency will be limited by:

  • Asymmetries in the input waveforms (outside our control). A perfect square wave is best
  • Time jitter in the sample time (within our control but not perfect)
  • Asymmetric turn-on and turn-off delays at the inputs caused by input EMC filters.

We have specified a maximum of 1.8kHz for most controller models. This can only be achieved if the input waveform is a perfectly symmetrical square wave (equal ON and OFF times). Unless you have an oscilloscope and can check your input waveform, it can be very difficult to know how symmetrical your input is. For example, a proximity switch or a photo cell on an encoder disk or cog wheel could produce a quite asymmetrical waveform, depending on its mechanical and sensitivity adjustments. Any input state, be it an on or off state, that lasts less than 277µS is likely to be missed.

Warning: The OBCB is liable to miss counts while the processor is writing to any of the non-volatile memories (non-volatile memory writes block the processor for up to several milliseconds). If your program writes to non-volatile memory you must make sure it does not happen at a time when it has to count input pulses.

Please see the documentation for the SPLat controller you are using for information on which inputs are used.


OBCB Theory of operation

The OnBoard Counter B allows event and/or frequency counting on up to 8 inputs (channels).

Count registers

Each channel has a 16-bit count register with a capacity of 65,535 counts. If the count goes over that it rolls over to zero. The count register can be read out as a floating point number, and optionally cleared at the same time.

Time base

For frequency measurement there is a single time base associated with (shared by) all OBCB channels. The time base works on a fixed cycle, consisting of two phases. Phase A lasts 1000mS, and Phase B lasts 100mS. You use Phase A as the counting phase and Phase B for reading out results. There are instructions to let you synchronize accurately to the two phase.

The accuracy of the readings will depend on timebase error and quantisation error. Typically this is:

  • For controllers that use a crystal for timing: 0.1% plus/minus 1 count.
  • For controllers that do not use a crystal for timing: 2% plus/minus 1 count.

jndexing of channels

The OBCB instructions contain the channel number as an argument. The channel number is jndexed, so it is very easy to create multichannel counting functions using MultiTrack.


OBCB Programming

Programming the OBCB is quite simple. The instructions are summarized below:

Starting and stopping counting.

You can start and stop counting either asynchronously (at any time) or in synchronism with the timebase A and B phases.

OBCB_Start   c     ;Start counting on channel c immediately
OBCB_StartA  c     ;Start counting on channel c when in phase A
OBCB_Stop    c     ;Stop counting on channel c immediately
OBCB_StopB   c     ;Stop counting on channel c when in phase B

Note: OBCB_StartA will start counting immediately if the timebase is already in phase A. OBCB_StopB will stop counting immediately if the timebase is already in phase B.

Reading out and clearing the counter

There are two instructions for reading out counts. One clears the counter. By clearing the counter simultaneously with reading it out, you can guarantee not to lose counts if you are totalizing them elsewhere.

OBCB_fRead   c     ;Read the contents of channel c to W as a floating point number
OBCB_fRdClr  c     ;Read the contents of channel c to W and clear the counter

Testing the timebase

Two instructions allow you to synchronize your program to the A and B phases of the timebase. You will need these only if you are measuring frequency (not counting events/items).

OBCB_GoIfA   llll  ;Go to program line llll if the time base is in phase A
OBCB_GoIfB   llll  ;Go to program line llll if the time base is in phase B


Examples: OBCB

In This Section

Example: OBCB counting off events

Example: OBCB frequency counting

Example: OBCB frequency measurement with long time base


Example: OBCB counting off events

The following program is a simple example of using the OnBoard Counter B to count off events and trigger some action after a certain accumulated count. In this instance imagine it is counting frozen pizzas as a conveyor moves them into an "outer" carton. For every 12 pizzas it triggers an output that moves the filled carton to a sealing machine.

;Pizza Packer.

        LaunchTask      Count12

        LaunchTask      BoxSealer

        RunTasksForever

;============= Pizza counting task =================================

;The pizza detector is connected to input "iPizzaCount", corresponding

;to OBCB channel "iPizzaCount". For every 12 pizzas we have to send a signal

;to the box Sealer task to generate an output pulse. This is done by

;a GoSub to SealBox

;Pizzas arrive at a rate of about one per second.

iPizzaCount     iEQU    5       ;Define the count channel

Count12:

        OBCB_Start      iPizzaCount

Loop12:

        YieldTask

        OBCB_fRead      iPizzaCount     ;Get the count

        fLoadQ          11

        fSUB                            ;-ve if count >= 12

        fGoIfPos        Loop12

Got12:

        OBCB_fRdClr     iPizzaCount     ;Clear the counter

        GoSub           SealBox         ;Trigger the box Sealer

        GoTo            Count12

;================== Box Sealer task. =================================

;Generate a 5 second pulse on output "oBoxSeal" whenever

;signalled by the pizza counting task. (In practice this task

;could itself be a complex sequence to control the folding

;and taping process).

oBoxSeal        oEQU    3       ;Define the Sealer trigger output

sSeal           defSEM

BoxSealer:

        WaitForST       sSeal           ;Wait for the signal semaphore

        ClrS            sSeal           ;Clear the semaphore for next time

        On              oBoxSeal        ;Start the Sealing sequence

        Pause           500             ;5 seconds for the sequence to complete

        Off             oBoxSeal        ;End the sequence

        GoTo            BoxSealer

;GoSub here to trigger one box Sealing cycle..

SealBox:

        SetS            sSeal

        Return

There are a couple of interesting points to note about this program:

  1. The test for 12 pizzas is done by testing for anything more than 11 pizzas. This is called defensive programming. Defensive programming is a particular form of paranoia that reduces the number of pizzas spilling on the floor if something goes wrong (and it will!).
  2. Because the box sealing and pizza counting functions are relegated to separate tasks, it is perfectly OK for the sealing task to take longer than the interval between two pizzas.
The counting task simply calls a subroutine, that is part of the sealing function, to trigger a sealing operation. It does not know, or care, how the sealing function works. This separation and hiding of function (and data) is a very important principle of structured, object oriented programming.


Example: OBCB frequency counting

An anemometer (wind speed gauge) sensor produces an output frequency between 0Hz and 125Hz. The equation for wind speed is

Wind speed = (Hz x 0.765) + 0.35 in meters/second

The following program will read the anemometer and display wind speed in mph.

;Anemometer

                LaunchTask      Anemometer

                RunTasksForever

;===== Anemometer task ============

iAnemometer     iEQU            0       ;Sensor connected to input 0

Anemometer:

                OBCB_StartA     iAnemometer     ;Start counting in A phase

An_B:

                YieldTask

                OBCB_GoIfB      An_B    ;Wait for A phase ...

                                        ; ... Counting has started

                OBCB_StopB      iAnemometer     ;Stop counting in B phase

An_A:

                YieldTask

                OBCB_GoIfA      An_A            ;Wait for B phase

;In B phase: Read out and reset

                OBCB_fRdClr     iAnemometer     ;Frequency (Hz) in W

                fLoadQ          0.765

                fMUL

                fLoadQ          0.35

                fADD                            ;Speed in m/s

                fLoadQ          3600

                fMUL                            ;m/h

                fLoadQ          1609.344        ;meters/mile

                fSwap

                fDIV                            ;mph

                OBLCD_SetCur    0,0

                OBLCD_fDispW    4,1

                OBLCD_Text      "mph"

                GoTo            Anemometer

The basic principle of frequency measurement is this:

  1. During phase B, you instruct it to start counting at the start of phase A.
  2. Wait for phase A
  3. During phase A, instruct it to stop counting at the start of phase B.
  4. Wait for phase B
  5. During phase B, read out and process the result, then loop (back to step 1).
Chances are that with this procedure the very first reading will be wrong. The next example has some initial synchronization to prevent that.


Example: OBCB frequency measurement with long time base

The OBCB has a fixed timebase of 1.1 seconds, of which 1 second is normally spent counting. That will give a resolution of 1 count in 1s, or 1Hz. Suppose you need to extend that to say 0.1Hz?

In a regular frequency counter you would use a 10s timebase, which would give you a resolution of 0.1Hz. The other counter, OBCA, has that ability, but not OBCB. What you can do, however, is accumulate counts over several time base cycles. For example, you could use 9 cycles. This would give a total count time of 8 times 1.1S, plus 1 second in the 9th cycle, for a total count period of 9.8s. You then divide the total count by 9.8 to get back to Hz.

;High resolution frequency counting with OBCB

                LaunchTask      HiResFreak

                RunTasksForever

;===== High resolution frequency measurement ============

;A new value is stored in fHRFreq every 9.8s

iHRF            iEQU            0       ;Sensor connected to input 0

fHRFreq:        defFLOAT

bHRFCount       defByte

HiResFreak:

HRF_0:

                YieldTask                       ;Initial sync to Phase A

                OBCB_GoIfB      HRF_0

HRF_1:

                YieldTask                       ;Initial sync to Phase B

                OBCB_GoIfA      HRF_1

HRF_Set2:

                SetMem          bHRFCount,8     ;Initialize (next) count sequence

                OBCB_StartA     iHRF            ;Start count on next A

HRF_2:

                YieldTask                       ;Wait for Phase A

                OBCB_GoIfB      HRF_2

HRF_3:

                YieldTask                       ;Phase A (repeats 8 times)

                OBCB_GoIfA      HRF_3

HRF_4:

                YieldTask                       ;Phase B (repeats 8 times)

                OBCB_GoIfB      HRF_4

                DecMGoIfNZ      bHRFCount,HRF_3 ;Count off A/B cycles

HRF_Set5:

                OBCB_StopB      iHRF            ;Prepare to stop counting after 9th A

HRF_5:

                YieldTask                       ;Wait for end of 9th A

                OBCB_GoIfA      HRF_5

                OBCB_fRdClr     iHRF            ;Get result

                fLoadQ          9.8             ;We've been counting this many seconds

                fSwap

                fDiv                            ;Scale to Hz

                fStore          fHRFreq         ;Save result

                GoTo            HRF_Set2        ;Loop back for the next reading

Notes:

  1. This program has two states at the start to ensure that the very first reading is fully synchronized to the A/B timebase phases, so there will be no spurious 1st reading.


Onboard Quadrature Counter

Previous Topic

Next Topic

In This Chapter

OBQC: Introduction

OBQC: Hardware

OBQC: Theory of operation

OBQC: Programming


OBQC: Introduction

The OnBoard Quadrature Counter (normally abbreviated to "OBQC") is an intrinsic Firmware function that is implemented on a number of SPLat controller boards. This guide covers the aspects of the OBQC that are common to all implementations.

The OBQC is a feature of the controller's operating firmware. Early releases of the MS12 and SL99 did not contain the OBQC in their firmware, but can be upgraded via the reFlash facility to incorporate the OBQC function. The MMi201 will have it from the beginning. The OBQC instructions are first found in dialect 16.

You should also read the OBQC specific material in the documentation for the specific controller (board) you are using.

The OBQC provides direction sensitive up/down counting for a quadrature encoder connected to two designated input pins. It provides a 24-bit counter and in most controllers for which it is implemented can count at up to 3,000 input transitions per second.

A quadrature encoder is an incremental encoder with two outputs, usually called A and B, which produce square wave signals 90 degrees out of phase. With such an encoder it is possible to determine both the amount of movement or rotation and the direction.

Hint: Use a quadrature encoder only if your application really does need to track direction. If all you need is the amount of movement, because the direction is fixed or known, there's no need to use quadrature encoding. If however mechanical imperfections can produce unpredictable movement a quadrature encoder may be appropriate.

See also the OnBoard Counter A and OnBoard Counter B for different counting capabilities.


OBQC: Hardware

There is no hardware exclusively devoted to the OBQC function. Basically, the OBQC comes "free" in the design of the SPLat board. On some custom boards, however, the OBQC may be associated with special purpose input conditioning circuitry. An example of that would be inputs designed to work off TTL levels rather than NPN sensors.

That said, the OBQC operates in conjunction with two hardware inputs. Usually this is simply a pair of regular inputs that can be used for counting via the OBQC or as a normal input. The ideal maximum count rate is 4kHz (input state changes per second), as determined by the Firmware's sampling rate of the inputs. The actual maximum frequency will be limited by:

  • Asymmetries in the input waveforms (outside our control)
  • Time jitter in the sample time (within our control but not perfect)
  • Asymmetric turn-on and turn-off delays at the inputs caused by input EMC filters. As an example, see MMi201 input response times

We have specified a maximum of 2.5kHz for most controller models. This is the number of input state changes per second (equal to the count rate). That will be 4 times the pulse frequency on either input channel. This can only be achieved if the input transitions are absolutely equally spaced. Unless you have an oscilloscope and can check your input waveforms, it can be very difficult to know how symmetrical your inputs are. For example, proximity switches or a photo cells on an encoder disk or cog wheel could produce a quite asymmetrical waveforms, depending on mechanical and sensitivity adjustments.

Warning: The OBQC is liable to miss counts while the processor is writing to any of the non-volatile memories (non-volatile memory writes block the processor for up to several milliseconds). If your program writes to non-volatile memory you must make sure it does not happen at a time when it has to track a quadrature encoder.

Please see the documentation for the SPLat controller you are using for information on which inputs are used.


OBQC: Theory of operation

A quadrature encoder is an incremental encoder that generates two output signals that are offset from each other. The signals are usually called A and B. In the above diagram you can see the A and B signals for clockwise encoder rotation (top) and counterclockwise encoder rotation (bottom). You will notice that in the top diagram a given transition on A happens before the same transition on B (A leads B). On the bottom diagram A lags behind B. The amount of lead or lag is 1/4 of a cycle (90 degrees), which is why the term quadrature encoder is used.

Because of the two quadrature signals it is possible to determine not only how far the encoder has moved or rotated, but also the direction. The encoder is called incremental because it does not provide information about the absolute position or angle, only about changes (increments). To know exactly where the encoder is you have to count pulses from a known starting point.

The OBQC function on a SPLat controller board tracks two nominated inputs (which inputs is documented for each product) and maintains a count in a 24-bit up/down counter. The value in the counter can be accessed via the floating point register W. The counter can be cleared, set to a given value and read out.

A 24-bit counter has a capacity of 16,777,216 counts. In SPLat that is treated as a signed number in the range -8,388,608 to +8,388,607. If one count represents 0.01mm (0.0004 inches) the range is +/- 83m or 272 feet. If the counter hits the maximum count it wraps to the minimum count, and visa versa. Given the range, that should not happen unless something has gone wrong in your machine or your program.

A mechanical computer mouse contains two quadrature encoders, one for X direction and one for Y direction. If you have an old mechanical mouse lying about it can be educational to pull it apart and analyze how the encoders work. It is sometimes possible to make your own encoder using photoelectric or proximity sensors and a convenient target such as a cog wheel or disk with holes cut in it. If you do so, remember it is crucial that the on and off times are very close to equal and that the two channels have the correct offset from each other.

The OBQC counts on each input transition, so it will accrue 4 counts per complete cycle of the inputs. There is no point trying to specify which connection and rotation will count up or down. This is best determined by trial and error on your prototype. If it counts in the wrong direction just swap the inputs.

A quadrature encoder doesn't have to be a rotary device. If is perfectly possible to make a linear motion encoder using the same principle.


OBQC: Programming

Programming for the OBQC is very simple. There is no initialization required. While the initial implementation supports only one counter, the programming instructions have provision for a counter number (which must always be 0 or can be omitted).

The instructions are:

OBQC_Clr		0

Clears the counter to 0. Typically your program would do this when an external home (reference position) switch activates.

OBQC_fGet	0

Reads the counter and leaves the result in W as a floating point number

OBQC_fSet	0

Copies the number in W into the counter. A good way to seek a particular position is to preset the counter to the required value such that during the movement it counts down to zero.

OBQC_Status    0

Used to test of the counter is missing counts, usually as a result of the input being faster than specified.


The CPU device

Previous Topic

Next Topic

In This Chapter

The CPU device

CPU: Firmware revision

CPU: Dialect

CPU: Board type

CPU: I/O configuration

CPU: Subroutine nesting depth

CPU: Runtime error codes

CPU: Processor reset flags

CPU: DTR input

CPU: Miscellaneous resources (D>=16)

CPU: RTS output

CPU: Altering SuperTimer 10mS timebase [D>=20]

CPU: Processor temperature


The CPU device

The CPU is a special "peripheral" that actually represents the SPLat processor itself. It allows programmatic access to a number of interesting things. We expect features to be added to this device as the need arises, so you should check back to this section from time to time to see what's new (assuming you are access the knowledge base online or updating your copy periodically).

The CPU is accessed via the U and V registers and the related low level UV transfer instructions and SPx communication instructions.

SPLat/PC recognizes !CPU as the address for this device in SPx communication instructions.

Which CPU attributes are supported will be documented in the Firmware release notes in the reFlash section of each board's documentation.


CPU: Firmware revision

The instruction

     SPxPoll2   0,!CPU

will return the board's current Firmware revision in U0 and U1. U0 is the major revision and U1 is the minor revision. Both numbers should be interpreted as unsigned decimal numbers.

Example: Display firmware version on the LCD:

     OBLCD_SetCur    0,0             ;Prepare LCD
     OBLCD_Text      "F/ware: "      ;Show a title
     SPxPoll2        0,!CPU          ;Get f/ware rev
     PushU           0               ;Major f/w
     OBLCD_UDecDispXVW               ;Display major
     OBLCD_Text      "."             ;Show a point
     PushU           1               ;Minor f/w
     OBLCD_UDecDispXVW               ;Display minor


CPU: Dialect

The instruction

     SPxPoll2   1,!CPU

will return the board's current dialect in U0 and U1. The actual dialect number is 256*U0 + U1. For dialects <255 U1 is all that you need worry about (which in practical terms means all dialects likely this century!).

Example:

     OBLCD_SetCur    1,0             ;Prepare LCD
     OBLCD_Text      "Dialect: "     ;Show a title
     SPxPoll2        1,!CPU          ;Get dialect
     PushU           1               ;Copy to X
     OBLCD_UDecDispXVW               ;Display


CPU: Board type

The instruction

     SPxPoll2   3,!CPU

will return the board's type number in U0 (high byte) and U1 (low byte). This is the same number as reported in the reFlash utility. It will vary with different numbers of XPice boards connected.

The I/O configuration provides more detailed information.


CPU: I/O configuration

The instruction

     SPxChIn   0,!CPU

will return the board's I/O configuration in U. This will vary with different numbers of XPice expansion boards connected. The items returned are:

U0 | Number of digital inputs
U1 | Number of digital outputs
U2 | Number of analog inputs
U3 | Number of analog outputs

Note that, in general, the numbers returned are the maxima for each type of resource. Pins on SPice connectors are configurable to more than one type of I/O. Each such configurable pin will be included in each of the possible of the above counts.

Example:

An SL99 without any expansion boards connected will return the numbers

U0=16 | Number of digital inputs
U1=14 | Number of digital outputs
U2=5 | Number of analog inputs
U3=3 | Number of analog outputs
  • The 14 inputs are 8 external inputs on the base board, 2 option links that appear as inputs and 6 SPice pins that can be configured as inputs.
  • The 14 outputs are 8 external outputs on the base board plus 6 SPice pins that can be configured as outputs.
  • The 5 analog inputs are 1 provided on the base board plus 4 SPice pins that can be configured as analog inputs.
  • The 3 analog outputs are 1 provided on the base board plus 2 SPice pins that can be configured as analog outputs. Of the latter 2, one (Analog output G) is also the thermistor excitation voltage.


CPU: Subroutine nesting depth

The instruction

     SPxPoll1   2,!CPU

will return the current subroutine stack pointer in U0. This is twice the number of levels of subroutine stacking. This can be useful during debugging to check how many level of nesting you have left.

Example: This program will "nest down" one level of subroutine each time push button input 8 is pressed on an MMi99, and display the nesting level on the LCD. The program will crash and restart when the subroutine stack overflows, and the displayed number will revert to 0. Expect an error report when you re-connect with SPLat/PC!

Again OBLCD_SetCur      0,0
      SPxPoll1          2,!CPU
      PushU             0
      RorM                       ;Binary divide by 2
      OBLCD_UDecDispXVW

      WaitOnK           8

      GoSub             Again

Note that we can use a RorM (rotate) as a divide by 2 because in this case we know the number we are dealing with is even. If it could be an odd number then an extra masking would be necessary, viz:

      RorM                       ;Binary divide by 2

      LoadX             '7F

      AndM


CPU: Runtime error codes

The instruction

     SPxPoll1   3,!CPU

will return the last recorded runtime error code in U0. The value will be zero if there has not been a runtime error. If there is a recorded runtime error, then the instruction

     SPxPoll2   2,!CPU

will return the address of the offending instruction in U0 (high byte) and U1 (low byte). You can reconstruct the hexadecimal address by displaying U0 followed by U1 in hexadecimal. This can then be related back to the .LST file generated when you translated the program.

The meanings of the runtime error codes are listed below. Please report to us if you get a code that is not listed.

The controller resets its error code when it is turned off. Hence, if you have a display on your controller you can implement a scheme that allows your users to report back any runtime errors. Runtime errors are errors generated by bugs in your program, so having the ability to capture elusive instances can be very useful.

Example

The following program is intended for an MMi99. It checks for an error at startup. If there is an error it displays it on the LCD then sits there making annoying beeps. If there is no error it proceeds to the main program which is a test of the subroutine nesting depth. That program allows you to generate a runtime error by exceeding the allowable nesting depth (push the lower push button to nest one level down). When an error is generated the SPLat board and program are restarted, but with the error code from the previous incarnation preserved.

;Testing runtime error codes in cpu device

         SPxPoll1       3,!CPU       ;At startup test for error

         PushU          0            ;Get the error code to X

         Push                        ;Duplicate

         GoIfZ          CarryOn      ;g/ no error

HaveError:

         OBLCD_SetCur   0,0

         OBLCD_Text     "Err "

         OBLCD_UDecDispXVW           ;Show error code

         OBLCD_Text     " at '"

         SPxPoll2       2,!CPU       ;Get error address

         PushU          0            ;MS byte

         OBLCD_HexDispX

         PushU          1            ;LS byte

         OBLCD_HexDispX

         OBLCD_SetCur   1,0

         OBLCD_Text     "Please report"

BeepLoop:

         On             8

         Pause          5

         Off            8

         Pause          100

         GoTo           BeepLoop

;This "main" program will nest one level down in the

;subroutine stack each time button 8 is pressed, until it crashes

;Test how deep the subroutine stack can be stacked.

;Displays a count on outputs 0-7

A        IncM           0

CarryOn:

         OBLCD_SetCur   0,0

         OBLCD_Text     "Nested "

         OBLCD_UDecDispMVW 0

         WaitOnK        8

         GoSub          A

         Return                    ;Stops translator complaining

Runtime error codes.

0 There is no runtime error logged.

1  Attempted to run past the end of the program -OR- Attempted to execute an Opcode (instruction) not recognized by this controller.

2  A BRANCH/TARGET went outside the program.

3  Attempt to RETURN when not in a subroutine.

4  TARGET list went off end of program memory.

5  Attempting to INPUT from a non-existent input.

6  Attempting to OUTPUT to a non-existent output.

7  Attempting to access a non-existent data memory.

8  Attempting to access a non-existent timer.

9  GOTO to non-existent location. Please notify SPLat with details of circumstances. Should be totally impossible except Firmware 1.0

10  GOSUBs nested deeper than permitted.

11  BRANCH with offset (X) more than 127.

12  GOSUB to non-existent location. Please notify SPLat with details of circumstances. Should be totally impossible except Firmware 1.0

13  BRANCH goes outside program.

14  The specified analog output is not available

15  The specified analog input is not available

16  Base address specified in a mask I/O instruction is not 0, 8, 16 ... etc.

17  Non-existent command code cc in SPx instruction.

18  Requested U location outside 0-19.

19  Bad RAM (data memory) address: falls outside existing RAM.

20  Illegal command code in SPx instruction.

21  SPx instruction not applicable to this peripheral.

22  SPx peripheral reported bad parameter value.

23  Illegal peripheral address in SPx0 instruction.

24  No response from SPx peripheral

25  Bad SPice pin configuration data

26  Shadow memory instruction attempted to access non-existent memory address.

27  A semaphore instruction attempted to access non-existent RAM.

28  A Suspend or Resume instruction attempted to access non-existent RAM.

29  NVEM command code is invalid

30  NVEM bad page select number

31 NVEM pointer out of bounds.

32 NVEM record number overflow error or greater than 255 when pushed onto stack.

33 NVEM RAM destination is out of bounds.

34 UVtoNV instruction runs off end of U.

35 Attempt to write to NVEM page 0 within 128 bytes of end of program.

36 Offset in U too large during Fix/Float to/from U instructions.

37 RTC option is faulty or not fitted.

38 RTC option is faulty or not fitted.

39 RTC event number is invalid.

40 Quadrature encoder channel invalid.

41 TSP string size 0 or >16

48 MultiTrack task queue already full when trying to schedule a task.

49 MultiTrack task queue empty when trying to run task queue.

50 MultiTrack task queue instruction encountered when task queue not running.

51 Unequal number of GoSubs and Returns in a MultiTrack task.

52 Attempting to execute a RunTasksForever instruction with no tasks in the MultiTrack task queue.

53 MultiTrack task queue empty during RunTasksForever.

54 MultiTrack task executed 256 instructions in a row with no yield.

56 Bad Opcode

65 Xwire: You attempted to set a slave address in the master mode or the address supplied is greater than 253

66 Xwire: Tx data range goes past end of RAM

67 Xwire: Attempting to send more data than allowed with this board

68 Xwire: Rx data range goes past end of RAM

69 Xwire: Attempting to receive more data than allowed with this board

70 Xwire: The configuration table in the master contains more than the allowed number of entries.

71 String communications - Bad destination identifier

72 String communications - Command executed without string somms protocol enabled

73 String communications - Tx buffer full during Tx operation

74 String communications - Count value too big during "PrintFill" and "PrintString" instructions

75 String to RAM - String does not fit into user register area

80 Invalid OBCB channel number


CPU: Processor reset flags

The instruction

     SPxPoll1   0,!CPU

will return the processor reset flags in U0. These flags indicate the cause of the most recent hardware reset of the processor chip. Note that these are the actual underlying processor, not the SPLat virtual processor. Note also that these only apply to products based on the 68HC908GP32 processor (SL99, MMi99, JM01). Please contact us if you intend to use these flags.

The flags are:

Bit | Meaning
0 | WarmBoot D>=20
1 | Low voltage reset. Processor received too little supply voltage
2 | Always 0
3 | Illegal address reset. Please report
4 | Illegal OpCode reset. Please report
5 | Watchdog timer reset. Please report
6 | Processor reset pin
7 | Power on reset

Of these, 7 is the one that should occur most frequently. 1 might suggest a power supply problem or hardware fault. 3, 4 and 5 would suggest a problem in our Firmware, and you should report these along with as much information as possible about how they came about.


CPU: DTR input

On controllers that provide a DTR (Data Terminal Ready) input, the instruction

   SPxPoll1   4,!CPU

will return the state of the input in U0. A True value in U0 indicates the DTR input is asserted.


CPU: Miscellaneous resources (D>=16)

The instruction

SPxPoll1        1,!CPU

will return a single byte in U0 that indicates various things:

Bit | Signifies
0 | RTC hardware present (D>=16)
1 | Unused
2 | Unused
3 | Unused
4 | Unused
5 | Unused
6 | Unused
7 | Unused

Note: Not all bits are relevant to all products and/or dialects.


CPU: RTS output

Some models of SPLat have an RTS (Request To Send) output. Normally this will be manipulated automatically as part of a communications function. However, for testing is is handy to be able to manipulate it programmatically.

The instruction

SPxCmd0       1,!CPU

will turn on the RTS output (if it exists)

The instruction

SPxCmd0       0,!CPU

will turn off the RTS output (if it exists)


CPU: Altering SuperTimer 10mS timebase [D>=20]

In dialects 20 or later you can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.


Programming reference

If you are just starting out programming SPLat for the first time, this is not the place to start unless your are a seasoned programmer. SPLat has a rich repertoire comprising over 400 instructions, and trying to tackle them head-on all at once would be quite overwhelming. The best place to start instead is with FastTrack, which allows you to perform simple sequencing and timing tasks using just 14 instructions. FastTrack will have you producing results in a couple of hours. Within and beyond FastTrack there are a number of tutorials and guides to assist you along the way. Just remember, as you advance your skills and confidence SPLat has all the power you may need for really sophisticated control tasks.

This programming reference provides definitions of all SPLat instructions, directives, precodes, hash commands and functions, communications control block items, and result codes. Most of the sections contain instructions listed against various criteria: Alphabetically, I/O, counting etc. Most instructions will appear in several categories. They have been placed in categories with the intention of helping you find something to fit a particular situation. For example, under Numerical comparison instructions you will find instructions for floating point comparisons as well as for testing a number and doing a conditional GoTo based on the result.

Instructions that can be used in indexed mode have, in the headings, an asterisk ( * ) after the instruction argument that gets the index register added to it, for example ShadowRead mm*,cc

Instructions that get jndexed in a MultiTrack task and/or are affected by the IasJ: precode, have, in the headings, a plus sign ( + ) after the address operand that gets affected.

Instructions that use automatic indexing have, in the headings, a hash sign ( # ) after the argument that gets the index register added to it.

Hash commands and hash functions have, in the headings, a hash sign at the start of the heading.

For the more recently introduced instructions the applicable language dialect is noted in bracket, e.g. [D>=16].

Please note that not all instructions are available in all SPLat boards, even if the dialect matches. There can be hardware differences between boards (such as having LCD support or RTC) in addition to dialect differences. The availability is noted in each instruction description. See also reFlashing regarding possibly upgrading a board to a more recent dialect.

This reference does not replace the tutorial style presentation in the Classic help file that is part of the SPLat/PC programming software.

Previous Topic

Next Topic

In This Chapter

Instructions arranged by function

Instructions arranged alphabetically

EC1 instructions

Active Templates - library modules on steroids

Directives

Precodes

Result codes

Communication Control Block elements

ModBus Master control script commands

SimpleHMI low level message codes


Instructions arranged by function

This section contains a number of instruction groupings. Many instructions will appear in two or more groups.

In This Section

Analog instructions

Arithmetic (8 bit integer) instructions

Counting (and related) instructions

Communications instructions

FastTrack instructions

Floating point instructions

GoTo, GoSub and related instructions

# Hash commands and functions

Index register instructions

Input and Output instructions (digital)

Logical instructions

Memory instructions

Multi-tasking support (Deprecated)

MultiTrack instructions

Non-volatile memory

Number comparison and testing instructions

Onboard LCD (OBLCD) instructions

Onboard counter A (OBCA) Instructions

Onboard quadrature counter

RealTime Clock

Semaphore instructions

Stack instructions

SPLat expansion framework generic instructions

Timing instructions

Xwire instructions

WarmBoot [D .= 20]


Analog instructions

In This Section

AnIn#

AnOut#

fAnIn c+ [D>=18]

fAnOut c+ [D>=18]


AnIn#

Analog input. The # must be replaced by a letter designating the required analog channel, e.g. AnInA

The analog quantity (voltage or current) applied to the designated channel is converted to an 8-bit integer number and Pushed into X. If your controller supports analog input resolution higher than 8 bits, the extra resolution will be lost. Use fAnIn instead (this applies to all later models like SL100, MS120, CC16/CC18 and MMi202).

Note: This instruction is not recommended for new designs in controllers that can support dialect 18 or later. Use fAnIn instead. It will not be supported in 32-bit controllers.

See also AnOut fAnIn fAnOut


AnOut#

Analog output. The # must be replaced by a letter designating the required analog channel, e.g. AnOutA

The 8-bit integer number in X is converted to an analog quantity (voltage or current, depending on the model of SPLat board) on the designated output channel. For the bidirectional channels on the OEM36 this becomes the voltage against which the corresponding input is compared.

Note: This instruction is not recommended for new designs in controllers that can support dialect 18 or later. Use fAnOut instead. It will not be supported in 32-bit controllers.

See also AnIn fAnOut fAnIn

If you are using the MMi200 please click here for information on a restriction on using this instruction.


fAnIn c+ [D>=18]

Reads analog input channel c as a normalized floating point number.

The analog value is returned in W as a normalized number. That means it is always between 0 and 1, representing a fraction of full scale value. For example, if the analog input range is 0-10V, 5V will give a value of 0.5. The actual number obtained is the closest approximation available given the resolution and accuracy of the analog input. The higher the resolution of the analog input, the closer the approximation will be.

This instruction breaks with our past practice of using letter designations for analog inputs and outputs. Instead we use numeric channel numbers. The correspondence between letter designation and channel number is simply that A=0, B=1, C=2 etc. The analog window in SPLat/PC from V7.18.0 shows both schemes.

Analog I/O using normalized floating point numbers has three advantages:

  1. It handles analog resolution greater than 8 bits. Because the old AnIn and Anout work through X, and X is an 8-bit register, they are limited to 8-bit I/O.
  2. If you write your program using only floating point analog I/O, if and when you switch to a SPLat controller with higher analog resolution, you program will work exactly as before but return higher resolution results.
  3. The instructions can be jndexed.

If you write your program using only floating point analog I/O, if and when you switch to a SPLat controller with higher analog resolution, your program will work exactly as before but return higher resolution results. However, this instruction cannot deliver more analog resolution than the board hardware is capable of. Please see the documentation for your particular board.

The channel number c will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 18

See also AnIn, AnOut, fAnOut


fAnOut c+ [D>=18]

Outputs the floating point number in W to analog output c.

W must be between 0 and 1, representing a fraction of full scale value. For example, if the analog output range is 0-10V, 0.5 in W will give 5V out (half scale). The actual analog output obtained is the closest approximation available given the resolution and accuracy of the analog output. The higher the resolution of the analog output, the closer the approximation will be.

For numbers outside the range 0 to 1 the result will be as shown in the following table:

W | Output
0 <= W < 1.0 | W * full scale
-1.0 < W < 0 | -W * full scale
1.0 <= W <= +Inf | Full scale
-Inf <= W <= -1.0 | Full scale
NaN | 0

This behaviour is closely analogous to the Fix instruction when used to convert a floating point value for use with AnOut.

This instruction breaks with our past practice of using letter designations for analog inputs and outputs. Instead we use numeric channel numbers. The correspondence between letter designation and channel number is simply that A=0, B=1, C=3 etc. The analog window in SPLat/PC from V7.18.0 shows both schemes.

Analog I/O using normalized floating point numbers has three advantages:

  1. It handles analog resolution greater than 8 bits. Because the old AnIn and Anout work through X, and X is an 8-bit register, they are limited to 8-bit I/O.
  2. If you write your program using only floating point analog I/O, if and when you switch to a SPLat controller with higher analog resolution, your program will work exactly as before but return higher resolution results.
  3. The instructions can be jndexed.

The channel number c will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

If you write your program using only floating point analog I/O, if and when you switch to a SPLat controller with higher analog resolution, your program will work exactly as before but return higher resolution results. However, this instruction cannot deliver more analog resolution than the board hardware is capable of. Please see the documentation for your particular board.

Dialect exclusions: Not available in dialects before 18

See also AnIn, AnOut, fAnin


Arithmetic (8 bit integer) instructions

In This Section

ADD [D>=23]

SUB [D>=23]

ADC [D>=23]

TestXeqY [D>=28]

TestXgeY [D>=28]

TestXgtY [D>=28]

TestXleY [D>=28]

TestXltY [D>=28]

TestXneY [D>=28]


ADD [D>=23]

Simple binary 8-bit add of X and Y. Pops the operands off the stack, leaves the result in X. If the addition produces a carry sets the carry flag.

The R register does double duty as result flags for binary arithmetic instructions.

From dialect 23 bit 0 of R is the carry flag. A carry out of an ADD, ADC, SUB or SBC instruction will set bit 0 of R.

The same bit 0 the acts as the carry input to ADC and SBC.

See also ADD, SUB, ADC, SBC


SUB [D>=23]

Binary 8-bit subtraction of X from Y. Pops the operands off the stack, leaves the result in X. If the subtraction produces a borrow, sets the carry flag.

The R register does double duty as result flags for binary arithmetic instructions.

From dialect 23 bit 0 of R is the carry flag. A carry out of an ADD, ADC, SUB or SBC instruction will set bit 0 of R.

The same bit 0 the acts as the carry input to ADC and SBC.

See also ADD, ADC, SBC, RtoX


ADC [D>=23]

Simple binary 8-bit add of X and Y, with carry in taken from bit 0 or R. Pops the operands off the stack, leaves the result in X. If the addition produces a carry sets R = 1.

The R register does double duty as result flags for binary arithmetic instructions.

From dialect 23 bit 0 of R is the carry flag. A carry out of an ADD, ADC, SUB or SBC instruction will set bit 0 of R.

The same bit 0 the acts as the carry input to ADC and SBC.

See also ADD, SUB, SBC, RtoX


TestXeqY [D>=28]

Push a TRUE (255) into X if X = Y (unsigned integer comparison). Otherwise push 0 (FALSE) into X. The initial values of X and Y are pushed to Y and Z respectively.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 28.

See also TestXeqY, TestXneY, TestXltY, TestXgtY, TestXleY, TestXgeY, Compare, CompareR


TestXgeY [D>=28]

Push a TRUE (255) into X if X >= Y (unsigned integer comparison). Otherwise push 0 (FALSE) into X. The initial values of X and Y are pushed to Y and Z respectively.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 28.

See also TestXeqY, TestXneY, TestXltY, TestXgtY, TestXleY, Compare, CompareR


TestXgtY [D>=28]

Push a TRUE (255) into X if X > Y (unsigned integer comparison). Otherwise push 0 (FALSE) into X. The initial values of X and Y are pushed to Y and Z respectively.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 28.

See also TestXeqY, TestXneY, TestXltY, TestXleY, TestXgeY, Compare, CompareR


TestXleY [D>=28]

Push a TRUE (255) into X if X <= Y (unsigned integer comparison). Otherwise push 0 (FALSE) into X. The initial values of X and Y are pushed to Y and Z respectively.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 28.

See also TestXeqY, TestXneY, TestXltY, TestXgtY, TestXgeY, Compare, CompareR


TestXltY [D>=28]

Push a TRUE (255) into X if X < Y (unsigned integer comparison). Otherwise push 0 (FALSE) into X. The initial values of X and Y are pushed to Y and Z respectively.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 28.

See also TestXeqY, TestXneY, TestXgtY, TestXleY, TestXgeY, Compare, CompareR


TestXneY [D>=28]

Push a TRUE (255) into X if X not equal to Y (unsigned integer comparison). Otherwise push 0 (FALSE) into X. The initial values of X and Y are pushed to Y and Z respectively.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 28.

See also TestXeqY, TestXltY, TestXgtY, TestXleY, TestXgeY, Compare, CompareR


Counting (and related) instructions

Counting instructions provide a variety of ways in which you can count internal or external events. External events might be the number of bottles on a conveyor, while internal events might be the number of times a timer has expired (maybe as part of an elapsed hour meter).

In This Section

iiChrPrintX dest# [D>=23]

DecI

DecM mm+

DecX

DecMGoIfNZ mm*+,LLLL

DecMRetIfNZ mm+

DecMRetIfZ mm+

DMGNZ mm*+,LLLL

fDec

fInc

iifPrintWVW dest#,f,p [D>=23]

iifPrintWFW dest#,f,p [D>=23]

GetCount7 [D= 4 to 15]

iiHexPrintX dest# [D>=23]

IncI

IncM mm+

IncX

OBCA_fRead

OBCA_GoIfNoRdg line

OBCA_GoIfRdg line

OBCA_GoSubIfRdg line

OBCA_Mode m

OBCA_RetIfNoRdg

OBCA_WaitRdg

OBCB_GoIfA l

OBCB_GoIfB LLLL [D>=19]

OBCB_fRead c [D>=19]

OBCB_fRdClr c [D>=19]

OBCB_Start c [D>=19]

OBCB_StartA c [D>=19]

OBCB_Stop c [D>=19]

OBCB_StopB c [D>=19]

OBQC_Clr c [D>=16]

OBQC_fGet c [D>=16]

OBQC_fSet c [D>=16]

OBQC_Status c [D>=17]

iiPrintFill dest#,count,value [D>=23]

iisPrintXFW dest# [D>=23]

iisPrintXVW dest# [D>=23]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]

SetCount7 [D= 4 to 15]


iiChrPrintX dest# [D>=23]

"Character Print X"

Send the code in X to dest. Useful for example for sending carriage return ('0D) and line feed ('0A) out of the serial port.. Pops X.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23

See also iifPrintWFW, iifPrintWVW, iiHexPrintX, iisPrintXFW, iisPrintXVW, iiuPrintXFW, iiuPrintXVW


DecI

Decrements the Index Register. If it is zero it will be 255 after the DecI.

Dialect restriction: This instruction is implemented only in dialects 6 or later.

See also IncI, DecX


DecM mm+

Decrement (count down by one) (the number in) memory location mm. This is an unsigned 8-bit decrement, so 0 decremented becomes 255.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.

See also IncM


DecX

Subtracts one from the number in X. If X is 0 before the DecX, X becomes 255 after the DecX.

See also IncX


DecMGoIfNZ mm*+,LLLL

(A FastTrack instruction) "Decrement Memory, go if non-zero"

Decrements the contents of (number in) data memory location mm and tests the result. If the result is non-zero, performs a GoTo to the program line named LLLL. If the result is zero, the program continues at the next program line (i.e. does not GoTo).

mm must be a valid data memory (RAM) address (depending on which controller you are using). LLLL must be a label (name) used on a program line.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

The shorter form (alias) DMGNZ may also be used.

Indexing: From dialect 15 this instruction can be indexed by adding 'i' in front of it.

See also SetMem


DecMRetIfNZ mm+

Decrement (count down by one) (the number in) memory location mm and return from subroutine if the result is non-zero. This is an unsigned 8-bit decrement, so 0 decremented becomes 255.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 11.


DecMRetIfZ mm+

Decrement (count down by one) (the number in) memory location mm and return from subroutine if the result is zero. This is an unsigned 8-bit decrement, so 0 decremented becomes 255.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 11.


DMGNZ mm*+,LLLL

This is an alias for (does exactly the same as) DecMGoIfNZ


fDec

Subtracts 1 from W. Q is unchanged.

fDec executes faster than subtracting one with explicit code.

Click here for Result Codes.

Dialect restriction: The fDec instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fInc


fInc

Adds 1 to W. Q is unchanged.

fInc executes faster than adding one with explicit code.

Click here for Result Codes.

Dialect restriction: The fInc instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fDec


iifPrintWVW dest#,f,p [D>=23]

"Floating point print W, variable width".

Formats the floating point number in W into an ASCII (readable) character string and sends the result to the destination called for in dest.

The result will be maximum f characters, with a maximum of p places after the decimal point. Similar to iifPrintWFW except any leading spaces are suppressed.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23

See also iiChrPrintX, iifPrintWFW, iiHexPrintX, iisPrintXFW, iisPrintXVW, iiuPrintXFW, iiuPrintXVW


iifPrintWFW dest#,f,p [D>=23]

"Floating point print W, fixed width".

Formats the floating point number in W into an ASCII (readable) character string and sends the result to the destination called for in dest.

The result will be exactly f characters, with a maximum of p places after the decimal point. Very similar to OBLCD_fDispW, except for where the result winds up.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23

See also iiChrPrintX, iifPrintWVW, iiHexPrintX, iisPrintXFW, iisPrintXVW, iiuPrintXFW, iiuPrintXVW


GetCount7 [D= 4 to 15]

Deleted from dialects 16 and later. Please use the OBCA instead.

Retrieves the value in the high speed counter and Pushes it into X. With MMi200 you can use this counter at the same time as analog outputs A and B.

Dialect restriction: GetCount7 is implemented only in dialects 4 or later.

See also SetCount7

Note: If you are using a second generation SPLat such as MMi99 or SL99, you should consider the OnBoard Counter type A (OBCA) features rather than this older feature. The OBCA gives you frequency counting to 8kHz and beyond.


iiHexPrintX dest# [D>=23]

Formats the value in X as an ASCII/hex (readable) string and sends the result to the destination called for in dest. The result is always two characters. Pops X.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23

See also iiChrPrintX, iifPrintWFW, iifPrintWVW, iisPrintXFW, iisPrintXVW, iiuPrintXFW, iiuPrintXVW


IncI

Increments the Index Register. If it is 255 (the largest number that can be represented in I) it will be 0 after the IncI.

See also DecI, IncX


IncM mm+

Increment (count up by one) (the number in) memory location mm. This is an unsigned 8-bit increment, so 255 incremented becomes 0.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


IncX

Adds one to the number in X. If X is 255 (the largest number which can be represented in X) before the IncX, X becomes 0 after the IncX. This is similar to a car odometer which rolls over from 999999 to 000000.

See also DecX


OBCB_GoIfA l

Go to program line LLLL if the OnBoard Counter B time base is in phase A. This provides a method for testing and synchronizing operations to the time base.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfB, OBCB_Start, OBCB_StartA, OBCB_Stop, OBCB_StopB, OBCB_fRead, OBCB_fRdClr

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBCB_GoIfB LLLL [D>=19]

Go to program line LLLL if the OnBoard Counter B time base is in phase B. This provides a method for testing and synchronizing operations to the time base.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfA, OBCB_Start, OBCB_StartA, OBCB_Stop, OBCB_StopB, OBCB_fRead, OBCB_fRdClr

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBCB_fRead c [D>=19]

Read OnBoard Counter B (Firmware) counter channel c to W as a floating point number.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfA, OBCB_GoIfB, OBCB_Start, OBCB_StartA, OBCB_Stop, OBCB_StopB, OBCB_fRdClr

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBCB_fRdClr c [D>=19]

Read OnBoard Counter B (Firmware) counter channel c to W as a floating point number, then clear the counter.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfA, OBCB_GoIfB, OBCB_Start, OBCB_StartA, OBCB_Stop, OBCB_StopB, OBCB_fRead

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBCB_Start c [D>=19]

Start counting pulses on counting input c using the OnBoard Counter B (Firmware) counter. Counting will start immediately.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfA, OBCB_GoIfB, OBCB_StartA, OBCB_Stop, OBCB_StopB, OBCB_fRead, OBCB_fRdClr

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBCB_StartA c [D>=19]

Start counting pulses on counting input c using the OnBoard Counter B (Firmware) counter. Counting will start when the OBCB time base is in phase A. If the time base is in phase A when the instruction is executed, counting starts at once. If it is in phase B, counting will start when it next enters phase A.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfA, OBCB_GoIfB, OBCB_Start, OBCB_Stop, OBCB_StopB, OBCB_fRead, OBCB_fRdClr

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBCB_Stop c [D>=19]

Stop counting pulses on counting input c using the OnBoard Counter B (Firmware) counter. Counting will stop immediately.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfA, OBCB_GoIfB, OBCB_Start, OBCB_StartA, OBCB_StopB, OBCB_fRead, OBCB_fRdClr

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBCB_StopB c [D>=19]

Stop counting pulses on counting input c using the OnBoard Counter B (Firmware) counter. Counting will stop when the OBCB time base is in phase B. If the time base is in phase B when the instruction is executed, counting stops at once. If it is in phase A, counting will stop when it next enters phase B.

OBCB provides a medium speed counting feature on multiple inputs of selected controllers. Please refer to specific controller documentation for details as well as OBCB generic documentation.

Dialect exclusions: Not available in dialects before 19.

Note: At time of writing there is no simulation of the OBCA in SPLat/PC

See also: OBCB_GoIfA, OBCB_GoIfB, OBCB_Start, OBCB_StartA, OBCB_Stop, OBCB_fRead, OBCB_fRdClr

Alternative counter mechanisms: OnBoard Counter A, OnBoard Quadrature Counter


OBQC_Clr c [D>=16]

Sets the onboard quadrature count channel c to 0. OBQC general documentation.

In current versions there is a single channel, so c must be 0 or can be left out.

Dialect exclusions: Not available in dialects before 16. Also, not available on SPLat controllers that do not have onboard quadrature counter support (please check the documentation for your specific board and Firmware version).

See also OBQC_fSet, OBQC_fGet, OBQC_Status, OBCA


OBQC_fGet c [D>=16]

Copies the onboard quadrature count channel c to W. OBQC general documentation.

In current versions there is a single channel, so c must be 0 or can be left out.

Dialect exclusions: Not available in dialects before 16. Also, not available on SPLat controllers that do not have onboard quadrature counter support (please check the documentation for your specific board and Firmware version).

See also OBQC_Clr, OBQC_fSet, OBQC_Status, OBCA


OBQC_fSet c [D>=16]

Copies the number in W to onboard quadrature count channel c. OBQC general documentation.

In current versions there is a single channel, so c must be 0 or can be left out.

Dialect exclusions: Not available in dialects before 16. Also, not available on SPLat controllers that do not have onboard quadrature counter support (please check the documentation for your specific board and Firmware version).

See also OBQC_Clr, OBQC_fGet, OBQC_Status, OBCA


OBQC_Status c [D>=17]

Push the OBQC status flag into X and clear the flag. This will be non-zero (True) if the counter has had a detectable miscount. OBQC general documentation.

In current versions there is a single channel, so c must be 0 or can be left out.

Dialect exclusions: Not available in dialects before 16. Also, not available on SPLat controllers that do not have onboard quadrature counter support (please check the documentation for your specific board and Firmware version).

See also OBQC_Clr, OBQC_fGet, OBCA


iiPrintFill dest#,count,value [D>=23]

Send count bytes of value to dest. If dest is in RAM, fills count bytes. If dest is the serial output, sends count characters.

count can be 1 to 255, so long as it doesn't overflow RAM (if RAM is the destination). The maximum number of bytes per message to the serial port is 255. However, the serial transmit buffer is 128 bytes (which could change in future Firmware revisions). If you try to send out more than the buffer can hold, your program will stall until there is room in the buffer (this is your whole program, not just a single MultiTrack task). You can avoid this by using COMTx_Space to ensure there is room before attempting to output.

Used mainly to clear a RAM data block. Typically RAM will be used as the destination when data is destined for an Xwire device.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23


iisPrintXFW dest# [D>=23]

"Signed print X, fixed width"

Interprets the 8-bit integer number in X as an signed number in the range -128 to +127. Formats it into an ASCII (readable) character string and sends the result to the destination called for in dest.. Pops X.

The result will be 4 characters with leading spaces for numbers with fewer than 3 digits. There will be a leading minus sign for negative numbers but no leading plus sign for positive numbers.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

The interpretation of negative numbers is standard "two complement".

Dialect exclusions: Not available in dialects before 23.

See also iiChrPrintX, iifPrintWFW, iifPrintWVW, iiHexPrintX, iisPrintXVW, iiuPrintXFW, iiuPrintXVW


iisPrintXVW dest# [D>=23]

"Signed print X, variable width"

Interprets the 8-bit integer number in X as an signed number in the range -128 to +127. Formats it into an ASCII (readable) character string and sends the result to the destination called for in dest.. Pops X.

The result will be a variable number of characters, depending on the size of the number. The minimum is 1 character (e.g. "9"), the maximum 4 (e.g. "-100").

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23

See also iiChrPrintX, iifPrintWFW, iifPrintWVW, iiHexPrintX, iisPrintXFW, iiuPrintXFW, iiuPrintXVW


iiuPrintXFW dest# [D>=23]

"Unsigned print X, fixed width"

Interprets the 8-bit integer number in X as an unsigned number in the range 0 to 255. Formats it into an ASCII (readable) character string and sends the result to the destination called for in dest. Pops X.

The result will be 3 characters with leading spaces for numbers <100. There will be no leading sign ("+").

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23

See also iiChrPrintX, iifPrintWFW, iifPrintWVW, iiHexPrintX, iisPrintXFW, iisPrintXVW, iiuPrintXVW


iiuPrintXVW dest# [D>=23]

"Unsigned print X, variable width"

Interprets the 8-bit integer number in X as an unsigned number in the range 0 to 255. Formats it into an ASCII (readable) character string and sends the result to the destination called for in dest.. Pops X.

The result will be a variable number of characters, depending on the size of the number. The minimum is 1 character, the maximum 3. There will be no leading sign ("+").

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect exclusions: Not available in dialects before 23

See also iiChrPrintX, iifPrintWFW, iifPrintWVW, iiHexPrintX, iisPrintXFW, iisPrintXVW, iiuPrintXFW


SetCount7 [D= 4 to 15]

Deleted from dialects 16 and later. Please use the OBCA instead.

Presets the high speed counter to the value in X, and pops the register stack. With MMi200 you can use this counter at the same time as analog outputs A and B.

Dialect restriction: SetCount7 is implemented only in dialects 4 or later.

See also GetCount7

Note: If you are using a second generation SPLat such as MMi99 or SL99, you should consider the OnBoard Counter type A (OBCA) features rather than this older feature. The OBCA gives you frequency counting to 8kHz and beyond.


Communications instructions

The communications instructions listed here are the actual SPLat language instructions. Getting communications going also involves setting up a Communications Control Block (CCB) and possibly (depending on the protocol) a control script.

Multiple serial ports

With dialect 29 we introduced a number of communication instructions that start with an 'a'. They all allow you to specify the address of the serial port. The reason for doing this is simply that we are using processor chips that support multiple serial ports (UARTS), and want to be able to give you access to them. The a-prefixed instruction all take an additional argument, the port address.

Port addresses are derived from the few unused 8-bit RAM addresses that exist. RAM addresses run from 0 through 249 (250 bytes). There is no RAM at 250 though 255.

The long-established programming port is located at address 251. On our older products this is an RS232-compatible serial port. On more recent 8-bit controllers (MS12x, CC18, etc) it is a serial port with 5V CMOS/TTL levels. On newer 32-bit designs some have a serial port with 3.3V CMOS/TTL levels and some (starting with the EC1) have a USB port instead. From a programming point of view these all respond to address 251.

Starting with the EC1, whose programming port at address 251 is USB, there is a second serial port available at address 252. This port uses 3.3V CMOS voltage levels but is 5V tolerant on the receive line.

Using the a-prefixed communications instructions with address 251 will give exactly the same result as using the "non-a" versions.

Hash commands

We introduced hash commands and hash functions to simplify certain aspects of programming SPLat. Quite a bit of this is aimed at serial communications. In effect "hash programming" gives you access to pre-written code for specific operations, using a simplified syntax and a big reduction in the number of lines of code you have to write. You should check out the available has commands before you start coding.

In This Section

aComRx_StrFind pp,c [D>=28]

ComRx_StrFind c [D>=27]

aComRx_StrFind2 pp,c [D>=29]

ComRx_StrFind2 c [D>=29]

ComDevAddr [D>=17]

aComDevAddr pp [D>=29]

ComHaltScript [D>=17]

ComResumeScript [D>=17]

ComRunScript pppp [D>=17]

ComSetCCB pppp [D>=17]

aComSetCCB pp,nnnn [D>=29]

ComTestStartTimer [D>=17]

Com_Flags [D>=23]

aCom_Flags pp [D>=29]

ComRx_BufLen [D>=23]

aComRx_BufLen pp [D>=29]

ComRx_DelXChars [D>=23]

aComRx_DelXChars pp [D>=29]

ComRx_fGetNum fw [D>=26]

aComRx_fGetNum pp,fw [D>=29]

ComRx_FindXInBuf (X) [D>=23]

aComRx_FindXInBuf (X) [D>=23]

ComRx_GetHex [D>=23]

aComRx_GetHex pp [D>=29]

ComRx_Peek (X) [D>=23]

aComRx_Peek pp [D>=29]

ComRx_ReadOne [D>=23]

aComRx_ReadOne pp [D>=29]

ComRx_Trim (X) [D>=23]

aComRx_Trim pp [D>=29]

ComTx_Space [D>=23]

aComTx_Space pp [D>=29]

iFindXInBuf bb*,nn [D>=28]

iiChrPrintX dest# [D>=23]

iifGetNum bb#,nn,fw [D>=28]

iifPrintWFW dest#,f,p [D>=23]

iifPrintWVW dest#,f,p [D>=23]

iiGetHex bb#,nn [D>=28]

iiHexPrintX dest# [D>=23]

iiPrintFill dest#,count,value [D>=23]

iiPrintNVText dest# [D>=27]

iiPrintText dest#,text-string [D >= 23]

iisPrintXFW dest# [D>=23]

iiStrFind bb#,nn,c [D>=28]

iiStrFind2 bb#,nn,c [D>=29]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]

iisPrintXVW dest# [D>=23]


aComRx_StrFind pp,c [D>=28]

Match a received string to a table in NVEM. This is subtly different to aComRx_StrFind2, which uses a different table format. The 'a' first letter of the keyword indicates that this is the "addressable" version, meaning the instruction can address a serial port other than the default.

This instruction seeks to match the data at the head of the serial Rx buffer for port pp with one or more "canned" strings stored in NVEM. Its primary use is for detecting received commands and machine generated ASCII messages. For example, we initially created it to assist in interacting with a cellular modem, for sending and receiving SMS messages.

If argument c is zero, the instruction treats upper and lower case letters as equivalent. If c is non-zero, the case of the received string must match exactly the string stored in NVEM.

The data in NVEM must be stored in a very specific "NULL terminated" format. This is best illustrated by an example. Lets say we want to detect which fruit has been requested, typed in on a terminal emulator and ending in a carriage return ('0D or 13)

1. NVEM: Message list

First you construct a list of all possible receive strings in NVEM. Each string must be terminated with a 0 byte. The 0 is not part of the string, it merely signals the end of the string (C-style NULL terminated strings).

strOrange:      NV0Byte   "Orange",13,0
strApple:       NV0Byte   "Apple",13,0
strBanana:      NV0Byte   "Banana",13,0
strLemon:       NV0Byte   "Lemon",13,0
strPear:        NV0Byte   "Pear",13,0
strGrape:       NV0Byte   "Grape",13,0
;        Terminator _________________^

2. NVEM: Pointer table

Next you construct a table, in NVEM, of pointers to the individual strings. This must be terminated in two 255 bytes. The 255s tell aComRx_StrFind that it has come to the end of the table.

FruitNames:     NV0Ptr    strOrange	;Entry #1
                NV0Ptr    strLemon 	;Entry #2
                NV0Ptr    strApple 	;Entry #3
                NV0Ptr    strPear  	;Entry #4
                NV0Ptr    strBanana	;Entry #5
                NV0Ptr    strGrape 	;Entry #6
                NV0Byte   255,255      ;<<<<< Essential!!!

You will notice that the order of entries is different to the order of the strings themselves. They can be in any order you like, and you can have several such tables, with different ordering, even different sets of strings.

3. Code: Wait for a message string

Now to your actual code (the above is tables, not executable code). The first thing your code needs to do is wait until it knows there's a string the the Rx buffer, ready to be interpreted. In this case we know that a carriage return signals the end of an incoming string in the Rx buffer. Hence, we might wait until there is a carriage return present. Let's say this is a MultiTrack task, so we simply write a loop that waits for the carriage return, thus:

COM1            EQU               252        ;Address of the required serial port
CRWait:         YieldTask
                LoadX             13         ;Carriage return
                aComRx_FindXInBuf COM1       ;Is there a CR in the Rx buffer?
                GoIfXEQ           255,CRWait ;loop back if not

This code snippet will sit and loop until a CR appears. Then it will "fall through". Once it falls through we now for sure that there is a message waiting for us (barring something going wrong!).

4. Code: Find a string match.

This is where we use the actual ComRx_StrFind instruction. First we have to make sure the NVEM access is correctly set up.

                NVSetPtr          FruitNames
                NVSetPage         0           ;0 is the default, so playing very safe!
                aComRx_StrFind    COM1,0      ;Seek a match between Rx buffer and the tabulated strings

5. Code: Evaluate the result.

The ComRx_StrFind returns its result in X, as follows:

  • If a match is found, the X-register contains the number of the entry in the pointer table, counting from 1. Thus, if in this case the received string was "Apple",13, X would contain 3.
  • If a match is not found, X will contain 0 (which makes Branch instruction very appropriate).

If a match is found, the matching string is removed from the Rx buffer. If no match is found the Rx buffer is not changed.

                Branch
                Target     NoMatch
                Target     RxOrange
                Target     RxLemon
                Target     RxApple
                Target     RxPear
                Target     RxBanana
                Target     RxGrape

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

You would place your own code to respond to each of the strings at RxOrange, RxLemon, etc. Quite likely you would then go back to CRWait and wait for the next incoming message.

6. Flush any garbage.

This step is what may make the difference between something that works perfectly in the lab but falters in the field. It handles the case of a message being received garbled. Serial communications is not a perfect medium ... things can and do go wrong over any communications medium. Even if you believe you have a perfect transmission channel, something as simple as a cable being plugged halfway through a message will corrupt it. An electrical noise spike can corrupt a character. If your program can get hung up because it can't detect a partial message, it will have to be restarted.

The strategy to overcome this risk is that whenever there should be a valid message in the Rx buffer (in our case when there's a carriage return character), but ComRx_StrFind is unable to decide what it is, we discard one single character from the head of the buffer. Remember, ComRx_StrFind will only detect a message that starts at the head of the buffer. One single stray character at the very start will "blind" it. So we "shave" character off the head until we either expose a valid message or we have emptied the buffer. This strategy will ensure that any corrupted messages get discarded. Other strategies are possible, depending on what is known about a given application.

In the above code the Branch target for no message is NoMatch. So ....

NoMatch:        aComRx_ReadOne	 COM1	      ;Shave one character off the head

                GoTo              CRWait     ;Go back and try again

Putting it all together

To make it easier to understand, the description above started with the NVEM tables and ended with the executable code. In a real program all NVEM data must come last, and be preceded by a NVEM0 directive. If this is all new to you, please see NVEM Documentation. Here it all is, in the correct order (this is still just a skeleton, as you must make sure it gets invoked in the manner best suited to your program, and you must of course add the actions required for each detected type of fruit):

;Wait for a carriage return character to appear in the Rx buffer

COM1            EQU               252        ;Address of the required serial port

CRWait:         YieldTask

                LoadX             13         ;Carriage Return

                aComRx_FindXInBuf COM1       ;Is there a CR in the Rx buffer?

                GoIfXEQ           255,CRWait ;loop back if not

;There's a CR in the buffer, hence a full message. Find a string match.

                NVSetPtr          FruitNames

                NVSetPage         0          ;0 is the default, so playing very safe!

                aComRx_StrFind    COM1,0     ;Seek a case insensitive match between Rx buffer and the tabulated strings

;Evaluate the result.

                Branch

                Target     NoMatch

                Target     RxOrange

                Target     RxLemon

                Target     RxApple

                Target     RxPear

                Target     RxBanana

                Target     RxGrape

;Actions

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

;Flush any garbage.

NoMatch:        aComRx_ReadOne	COM1	 ;Shave one character off the head

                GoTo             CRWait ;Go back and try again

;===================================================================

                NVEM0        ;Start of NonVolatile MEMory region 0

;Messages/strings

strOrange:      NV0Byte   "Orange",13,0

strApple:       NV0Byte   "Apple",13,0

strBanana:      NV0Byte   "Banana",13,0

strLemon:       NV0Byte   "Lemon",13,0

strPear:        NV0Byte   "Pear",13,0

strGrape:       NV0Byte   "Grape",13,0

;Table of pointers

FruitNames:     NV0Ptr    strOrange	;Entry #1

                NV0Ptr    strLemon 	;Entry #2

                NV0Ptr    strApple 	;Entry #3

                NV0Ptr    strPear  	;Entry #4

                NV0Ptr    strBanana	;Entry #5

                NV0Ptr    strGrape 	;Entry #6

                NV0Byte   255,255      ;<<<<< Essential!!!

Notes/reminders:

  1. The instruction only removes from the buffer the characters of any string it matches.
  2. The string must be at the very head of the buffer.
  3. The instruction argument controls case sensitivity
  4. Each string in NVEM must end in a 0 (NULL) byte.
  5. The table of pointers must end in two 255 bytes.
  6. The record length and record number pointers are not involved in this instruction. Only the page and pointer matter.

Dialect exclusions: Not available in dialects before 28

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_StrFind c [D>=27]

Match a received string to a table in NVEM. This is subtly different to ComRx_StrFind2, which uses a different table format.

This instruction seeks to match the data at the head of the default serial Rx buffer with one or more "canned" strings stored in NVEM. Its primary use is for detecting received commands and machine generated ASCII messages. For example, we initially created it to assist in interacting with a cellular modem, for sending and receiving SMS messages.

If argument c is zero, the instruction treats upper and lower case letters as equivalent. If c is non-zero, the case of the received string must match exactly the string stored in NVEM.

The data in NVEM must be stored in a very specific "NULL terminated" format. This is best illustrated by an example. Lets say we want to detect which fruit has been requested, typed in on a terminal emulator and ending in a carriage return ('0D or 13)

1. NVEM: Message list

First you construct a list of all possible receive strings in NVEM. Each string must be terminated with a 0 byte. The 0 is not part of the string, it merely signals the end of the string (C-style NULL terminated strings).

strOrange:      NV0Byte   "Orange",13,0
strApple:       NV0Byte   "Apple",13,0
strBanana:      NV0Byte   "Banana",13,0
strLemon:       NV0Byte   "Lemon",13,0
strPear:        NV0Byte   "Pear",13,0
strGrape:       NV0Byte   "Grape",13,0
;        Terminator _________________^

2. NVEM: Pointer table

Next you construct a table, in NVEM, of pointers to the individual strings. This must be terminated in two 255 bytes. The 255s tell ComRx_StrFind that it has come to the end of the table.

FruitNames:     NV0Ptr    strOrange	;Entry #1
                NV0Ptr    strLemon 	;Entry #2
                NV0Ptr    strApple 	;Entry #3
                NV0Ptr    strPear  	;Entry #4
                NV0Ptr    strBanana	;Entry #5
                NV0Ptr    strGrape 	;Entry #6
                NV0Byte   255        ;<<<<< Essential!!!

You will notice that the order of entries is different to the order of the strings themselves. They can be in any order you like, and you can have several such tables, with different ordering, even different sets of strings.

3. Code: Wait for a message string

Now to your actual code (the above is tables, not executable code). The first thing your code needs to do is wait until it knows there's a string the the Rx buffer, ready to be interpreted. In this case we know that a carriage return signals the end of an incoming string in the Rx buffer. Hence, we might wait until there is a carriage return present. Let's say this is a MultiTrack task, so we simply write a loop that waits for the carriage return, thus:

CRWait:         YieldTask
                LoadX      13         ;Carriage return
                ComRx_FindXInBuf      ;Is there a CR in the Rx buffer?
                GoIfXEQ    255,CRWait ;loop back if not

This code snippet will sit and loop until a CR appears. Then it will "fall through". Once it falls through we now for sure that there is a message waiting for us (barring something going wrong!).

4. Code: Find a string match.

This is where we use the actual ComRx_StrFind instruction. First we have to make sure the NVEM access is correctly set up.

                NVSetPtr   FruitNames
                NVSetPage  0           ;0 is the default, so playing very safe!
                ComRx_StrFind          ;Seek a match between Rx buffer and the tabulated strings

5. Code: Evaluate the result.

The ComRx_StrFind returns its result in X, as follows:

  • If a match is found, the X-register contains the number of the entry in the pointer table, counting from 1. Thus, if in this case the received string was "Apple",13, X would contain 3.
  • If a match is not found, X will contain 0 (which makes Branch instruction very appropriate).

If a match is found, the matching string is removed from the Rx buffer. If no match is found the Rx buffer is not changed.

                Branch
                Target     NoMatch
                Target     RxOrange
                Target     RxLemon
                Target     RxApple
                Target     RxPear
                Target     RxBanana
                Target     RxGrape

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

You would place your own code to respond to each of the strings at RxOrange, RxLemon, etc. Quite likely you would then go back to CRWait and wait for the next incoming message.

6. Flush any garbage.

This step is what may make the difference between something that works perfectly in the lab but falters in the field. It handles the case of a message being received garbled. Serial communications is not a perfect medium ... things can and do go wrong over any communications medium. Even if you believe you have a perfect transmission channel, something as simple as a cable being plugged halfway through a message will corrupt it. An electrical noise spike can corrupt a character. If your program can get hung up because it can't detect a partial message, it will have to be restarted.

The strategy to overcome this risk is that whenever there should be a valid message in the Rx buffer (in our case when there's a carriage return character), but ComRx_StrFind is unable to decide what it is, we discard one single character from the head of the buffer. Remember, ComRx_StrFind will only detect a message that starts at the head of the buffer. One single stray character at the very start will "blind" it. So we "shave" character off the head until we either expose a valid message or we have emptied the buffer. This strategy will ensure that any corrupted messages get discarded. Other strategies are possible, depending on what is known about a given application.

In the above code the Branch target for no message is NoMatch. So ....

NoMatch:        ComRx_ReadOne		;Shave one character off the head

                GoTo       CRWait      ;Go back and try again

Putting it all together

To make it easier to understand, the description above started with the NVEM tables and ended with the executable code. In a real program all NVEM data must come last, and be preceded by a NVEM0 directive. If this is all new to you, please see NVEM Documentation. Here it all is, in the correct order (this is still just a skeleton, as you must make sure it gets invoked in the manner best suited to your program, and you must of course add the actions required for each detected type of fruit):

;Wait for a carriage return character to appear in the Rx buffer

CRWait:         YieldTask

                LoadX      13         ;Carriage Return

                ComRx_FindXInBuf      ;Is there a CR in the Rx buffer?

                GoIfXEQ    255,CRWait ;loop back if not

;There's a CR in the buffer, hence a full message. Find a string match.

                NVSetPtr   FruitNames

                NVSetPage  0           ;0 is the default, so playing very safe!

                ComRx_StrFind  0       ;Seek a case insensitive match between Rx buffer and the tabulated strings

;Evaluate the result.

                Branch

                Target     NoMatch

                Target     RxOrange

                Target     RxLemon

                Target     RxApple

                Target     RxPear

                Target     RxBanana

                Target     RxGrape

;Actions

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

;Flush any garbage.

NoMatch:        ComRx_ReadOne		;Shave one character off the head

                GoTo       CRWait      ;Go back and try again

;===================================================================

                NVEM0        ;Start of NonVolatile MEMory region 0

;Messages/strings

strOrange:      NV0Byte   "Orange",13,0

strApple:       NV0Byte   "Apple",13,0

strBanana:      NV0Byte   "Banana",13,0

strLemon:       NV0Byte   "Lemon",13,0

strPear:        NV0Byte   "Pear",13,0

strGrape:       NV0Byte   "Grape",13,0

;Table of pointers

FruitNames:     NV0Ptr    strOrange	;Entry #1

                NV0Ptr    strLemon 	;Entry #2

                NV0Ptr    strApple 	;Entry #3

                NV0Ptr    strPear  	;Entry #4

                NV0Ptr    strBanana	;Entry #5

                NV0Ptr    strGrape 	;Entry #6

                NV0Byte   255,255      ;<<<<< Essential!!!

Notes/reminders:

  1. The instruction only removes from the buffer the characters of any string it matches.
  2. The string must be at the very head of the buffer.
  3. The instruction argument controls case sensitivity
  4. Each string in NVEM must end in a 0 (NULL) byte.
  5. The table of pointers must end in two 255 bytes.
  6. The record length and record number pointers are not involved in this instruction. Only the page and pointer matter.


aComRx_StrFind2 pp,c [D>=29]

Match a received string to a table in NVEM. This is subtly different to aComRx_StrFind, which uses a different table format. The 'a' first letter of the keyword indicates that this is the "addressable" version, meaning the instruction can address a serial port other than the default.

This instruction seeks to match the data at the head of the serial Rx buffer for port pp with one or more "canned" strings stored in NVEM. Its primary use is for detecting received commands and machine generated binary coded messages. For example, we initially created it to assist in programming the 4D brand displays.

If argument c is zero, the instruction treats upper and lower case letters as equivalent. If c is non-zero, the case of the received string must match exactly the string stored in NVEM.

The data in NVEM must be stored in a very specific Count/Data format.. This is best illustrated by an example. Lets say we want to detect which fruit has been requested, typed in on a terminal emulator and ending in a carriage return ('0D or 13)

1. NVEM: Message list

First you construct a list of all possible receive strings in NVEM. Each string must be preceded by a byte containing the length of the string itself.

strOrange:      NV0Byte   7,"Orange",13
strApple:       NV0Byte   6,"Apple",13
strBanana:      NV0Byte   7,"Banana",13
strLemon:       NV0Byte   6,"Lemon",13
strPear:        NV0Byte   5,"Pear",13
strGrape:       NV0Byte   6,"Grape",13
;   Byte count ___________^

2. NVEM: Pointer table

Next you construct a table, in NVEM, of pointers to the individual strings. This must be terminated in two 255 bytes. The 255s tell aComRx_StrFind2 that it has come to the end of the table.

FruitNames:     NV0Ptr    strOrange	;Entry #1
                NV0Ptr    strLemon 	;Entry #2
                NV0Ptr    strApple 	;Entry #3
                NV0Ptr    strPear  	;Entry #4
                NV0Ptr    strBanana	;Entry #5
                NV0Ptr    strGrape 	;Entry #6
                NV0Byte   255,255      ;<<<<< Essential!!!

You will notice that the order of entries is different to the order of the strings themselves. They can be in any order you like, and you can have several such tables, with different ordering, even different sets of strings.

3. Code: Wait for a message string

Now to your actual code (the above is tables, not executable code). The first thing your code needs to do is wait until it knows there's a string the the Rx buffer, ready to be interpreted. In this case we know that a carriage return signals the end of an incoming string in the Rx buffer. Hence, we might wait until there is a carriage return present. Let's say this is a MultiTrack task, so we simply write a loop that waits for the carriage return, thus:

COM1            EQU               252        ;Address of the required serial port
CRWait:         YieldTask
                LoadX             13         ;Carriage return
                aComRx_FindXInBuf COM1       ;Is there a CR in the Rx buffer?
                GoIfXEQ           255,CRWait ;loop back if not

This code snippet will sit and loop until a CR appears. Then it will "fall through". Once it falls through we now for sure that there is a message waiting for us (barring something going wrong!).

4. Code: Find a string match.

This is where we use the actual COMRx_StrFind instruction. First we have to make sure the NVEM access is correctly set up.

                NVSetPtr          FruitNames
                NVSetPage         0           ;0 is the default, so playing very safe!
                aComRx_StrFind2   COM1,0      ;Seek a match between Rx buffer and the tabulated strings

5. Code: Evaluate the result.

The ComRx_StrFind returns its result in X, as follows:

  • If a match is found, the X-register contains the number of the entry in the pointer table, counting from 1. Thus, if in this case the received string was "Apple",13, X would contain 3.
  • If a match is not found, X will contain 0 (which makes Branch instruction very appropriate).

If a match is found, the matching string is removed from the Rx buffer. If no match is found the Rx buffer is not changed.

                Branch
                Target     NoMatch
                Target     RxOrange
                Target     RxLemon
                Target     RxApple
                Target     RxPear
                Target     RxBanana
                Target     RxGrape

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

You would place your own code to respond to each of the strings at RxOrange, RxLemon, etc. Quite likely you would then go back to CRWait and wait for the next incoming message.

6. Flush any garbage.

This step is what may make the difference between something that works perfectly in the lab but falters in the field. It handles the case of a message being received garbled. Serial communications is not a perfect medium ... things can and do go wrong over any communications medium. Even if you believe you have a perfect transmission channel, something as simple as a cable being plugged halfway through a message will corrupt it. An electrical noise spike can corrupt a character. If your program can get hung up because it can't detect a partial message, it will have to be restarted.

The strategy to overcome this risk is that whenever there should be a valid message in the Rx buffer (in our case when there's a carriage return character), but ComRx_StrFind is unable to decide what it is, we discard one single character from the head of the buffer. Remember, ComRx_StrFind will only detect a message that starts at the head of the buffer. One single stray character at the very start will "blind" it. So we "shave" character off the head until we either expose a valid message or we have emptied the buffer. This strategy will ensure that any corrupted messages get discarded. Other strategies are possible, depending on what is known about a given application.

In the above code the Branch target for no message is NoMatch. So ....

NoMatch:        aComRx_ReadOne	 COM1	      ;Shave one character off the head

                GoTo              CRWait     ;Go back and try again

Putting it all together

To make it easier to understand, the description above started with the NVEM tables and ended with the executable code. In a real program all NVEM data must come last, and be preceded by a NVEM0 directive. If this is all new to you, please see NVEM Documentation. Here it all is, in the correct order (this is still just a skeleton, as you must make sure it gets invoked in the manner best suited to your program, and you must of course add the actions required for each detected type of fruit):

;Wait for a carriage return character to appear in the Rx buffer

COM1            EQU               252        ;Address of the required serial port

CRWait:         YieldTask

                LoadX             13         ;Carriage Return

                aComRx_FindXInBuf COM1       ;Is there a CR in the Rx buffer?

                GoIfXEQ           255,CRWait ;loop back if not

;There's a CR in the buffer, hence a full message. Find a string match.

                NVSetPtr          FruitNames

                NVSetPage         0          ;0 is the default, so playing very safe!

                aComRx_StrFind2   COM1,0     ;Seek a case insensitive match between Rx buffer and the tabulated strings

;Evaluate the result.

                Branch

                Target     NoMatch

                Target     RxOrange

                Target     RxLemon

                Target     RxApple

                Target     RxPear

                Target     RxBanana

                Target     RxGrape

;Actions

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

;Flush any garbage.

NoMatch:        aComRx_ReadOne	COM1	 ;Shave one character off the head

                GoTo             CRWait ;Go back and try again

;===================================================================

                NVEM0        ;Start of NonVolatile MEMory region 0

;Messages/strings

strOrange:      NV0Byte   7,"Orange",13

strApple:       NV0Byte   6,"Apple",13

strBanana:      NV0Byte   7,"Banana",13

strLemon:       NV0Byte   6,"Lemon",13

strPear:        NV0Byte   5,"Pear",13

strGrape:       NV0Byte   6,"Grape",13

;Table of pointers

FruitNames:     NV0Ptr    strOrange	;Entry #1

                NV0Ptr    strLemon 	;Entry #2

                NV0Ptr    strApple 	;Entry #3

                NV0Ptr    strPear  	;Entry #4

                NV0Ptr    strBanana	;Entry #5

                NV0Ptr    strGrape 	;Entry #6

                NV0Byte   255,255      ;<<<<< Essential!!!

Notes/reminders:

  1. The instruction only removes from the buffer the characters of any string it matches.
  2. The string must be at the very head of the buffer.
  3. The instruction argument controls case sensitivity
  4. Each string in NVEM must be preceded by a count byte.
  5. The table of pointers must end in two 255 bytes.
  6. The record length and record number pointers are not involved in this instruction. Only the page and pointer matter.

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_StrFind2 c [D>=29]

Match a received string to a table in NVEM. This is subtly different to ComRx_StrFind, which uses a different table format.

This instruction seeks to match the data at the head of the default serial Rx buffer with one or more "canned" strings stored in NVEM. Its primary use is for detecting received commands and machine generated binary coded messages.

If argument c is zero, the instruction treats upper and lower case letters as equivalent. If c is non-zero, the case of the received string must match exactly the string stored in NVEM.

The data in NVEM must be stored in a very specific Count/Data format.. This is best illustrated by an example. Lets say we want to detect which fruit has been requested, typed in on a terminal emulator and ending in a carriage return ('0D or 13)

1. NVEM: Message list

First you construct a list of all possible receive strings in NVEM. Each string must be preceded by a byte containing the length of the string itself.

strOrange:      NV0Byte   7,"Orange",13
strApple:       NV0Byte   6,"Apple",13
strBanana:      NV0Byte   7,"Banana",13
strLemon:       NV0Byte   6,"Lemon",13
strPear:        NV0Byte   5,"Pear",13
strGrape:       NV0Byte   6,"Grape",13
;   Byte count ___________^

2. NVEM: Pointer table

Next you construct a table, in NVEM, of pointers to the individual strings.This must be terminated in two 255 bytes. The 255s tell ComRx_StrFind2 that it has come to the end of the table.

FruitNames:     NV0Ptr    strOrange	;Entry #1
                NV0Ptr    strLemon 	;Entry #2
                NV0Ptr    strApple 	;Entry #3
                NV0Ptr    strPear  	;Entry #4
                NV0Ptr    strBanana	;Entry #5
                NV0Ptr    strGrape 	;Entry #6
                NV0Byte   255,255        ;<<<<< Essential!!!

You will notice that the order of entries is different to the order of the strings themselves. They can be in any order you like, and you can have several such tables, with different ordering, even different sets of strings.

3. Code: Wait for a message string

Now to your actual code (the above is tables, not executable code). The first thing your code needs to do is wait until it knows there's a string the the Rx buffer, ready to be interpreted. In this case we know that a carriage return signals the end of an incoming string in the Rx buffer. Hence, we might wait until there is a carriage return present. Let's say this is a MultiTrack task, so we simply write a loop that waits for the carriage return, thus:

CRWait:         YieldTask
                LoadX      13         ;Carriage return
                ComRx_FindXInBuf      ;Is there a CR in the Rx buffer?
                GoIfXEQ    255,CRWait ;loop back if not

This code snippet will sit and loop until a CR appears. Then it will "fall through". Once it falls through we now for sure that there is a message waiting for us (barring something going wrong!).

4. Code: Find a string match.

This is where we use the actual ComRx_StrFind instruction. First we have to make sure the NVEM access is correctly set up.

                NVSetPtr   FruitNames
                NVSetPage  0           ;0 is the default, so playing very safe!
                ComRx_StrFind          ;Seek a match between Rx buffer and the tabulated strings

5. Code: Evaluate the result.

The ComRx_StrFind returns its result in X, as follows:

  • If a match is found, the X-register contains the number of the entry in the pointer table, counting from 1. Thus, if in this case the received string was "Apple",13, X would contain 3.
  • If a match is not found, X will contain 0 (which makes Branch instruction very appropriate).

If a match is found, the matching string is removed from the Rx buffer. If no match is found the Rx buffer is not changed.

                Branch
                Target     NoMatch
                Target     RxOrange
                Target     RxLemon
                Target     RxApple
                Target     RxPear
                Target     RxBanana
                Target     RxGrape

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

You would place your own code to respond to each of the strings at RxOrange, RxLemon, etc. Quite likely you would then go back to CRWait and wait for the next incoming message.

6. Flush any garbage.

This step is what may make the difference between something that works perfectly in the lab but falters in the field. It handles the case of a message being received garbled. Serial communications is not a perfect medium ... things can and do go wrong over any communications medium. Even if you believe you have a perfect transmission channel, something as simple as a cable being plugged halfway through a message will corrupt it. An electrical noise spike can corrupt a character. If your program can get hung up because it can't detect a partial message, it will have to be restarted.

The strategy to overcome this risk is that whenever there should be a valid message in the Rx buffer (in our case when there's a carriage return character), but ComRx_StrFind is unable to decide what it is, we discard one single character from the head of the buffer. Remember, ComRx_StrFind will only detect a message that starts at the head of the buffer. One single stray character at the very start will "blind" it. So we "shave" character off the head until we either expose a valid message or we have emptied the buffer. This strategy will ensure that any corrupted messages get discarded. Other strategies are possible, depending on what is known about a given application.

In the above code the Branch target for no message is NoMatch. So ....

NoMatch:        ComRx_ReadOne		;Shave one character off the head

                GoTo       CRWait      ;Go back and try again

Putting it all together

To make it easier to understand, the description above started with the NVEM tables and ended with the executable code. In a real program all NVEM data must come last, and be preceded by a NVEM0 directive. If this is all new to you, please see NVEM Documentation. Here it all is, in the correct order (this is still just a skeleton, as you must make sure it gets invoked in the manner best suited to your program, and you must of course add the actions required for each detected type of fruit):

;Wait for a carriage return character to appear in the Rx buffer

CRWait:         YieldTask

                LoadX      13         ;Carriage Return

                ComRx_FindXInBuf      ;Is there a CR in the Rx buffer?

                GoIfXEQ    255,CRWait ;loop back if not

;There's a CR in the buffer, hence a full message. Find a string match.

                NVSetPtr   FruitNames

                NVSetPage  0           ;0 is the default, so playing very safe!

                ComRx_StrFind2  0       ;Seek a case insensitive match between Rx buffer and the tabulated strings

;Evaluate the result.

                Branch

                Target     NoMatch

                Target     RxOrange

                Target     RxLemon

                Target     RxApple

                Target     RxPear

                Target     RxBanana

                Target     RxGrape

;Actions

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

;Flush any garbage.

NoMatch:        ComRx_ReadOne		;Shave one character off the head

                GoTo       CRWait      ;Go back and try again

;===================================================================

                NVEM0        ;Start of NonVolatile MEMory region 0

;Messages/strings

strOrange:      NV0Byte   7,"Orange",13

strApple:       NV0Byte   6,"Apple",13

strBanana:      NV0Byte   7,"Banana",13

strLemon:       NV0Byte   6,"Lemon",13

strPear:        NV0Byte   5,"Pear",13

strGrape:       NV0Byte   6,"Grape",13

;Table of pointers

FruitNames:     NV0Ptr    strOrange	;Entry #1

                NV0Ptr    strLemon 	;Entry #2

                NV0Ptr    strApple 	;Entry #3

                NV0Ptr    strPear  	;Entry #4

                NV0Ptr    strBanana	;Entry #5

                NV0Ptr    strGrape 	;Entry #6

                NV0Byte   255,255      ;<<<<< Essential!!!

Notes/reminders:

  1. The instruction only removes from the buffer the characters of any string it matches.
  2. The string must be at the very head of the buffer.
  3. The instruction argument controls case sensitivity
  4. Each string in NVEM must be preceded by a count byte.
  5. The table of pointers must end in two 255 bytes.
  6. The record length and record number pointers are not involved in this instruction. Only the page and pointer matter.

Dialect exclusions: Not available in dialects before 29


ComDevAddr [D>=17]

Set the controller's communication device address to the value in X, overriding the address set in the current CCB. This allows runtime address setting without having to write NVEM. Pops X.

Note: This instruction will override a previous ComSetCCB. However, a subsequent ComSetCCB will in turn prevail. Normally you would therefore execute the required ComSetCCB and then do the ComDevAddr.


aComDevAddr pp [D>=29]

Set the controller's communication device address for the protocol running on serial port at address pp to the value in X, overriding the address set in the current CCB for that serial port. This allows runtime address setting without having to write NVEM. Pops X.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Note: This instruction will override the device address established in a previously executed aCom SetCCB. However, a subsequent aComSetCCB will in turn prevail. Normally you would therefore execute the required aComSetCCB and then do the aCom DevAddr.

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComHaltScript [D>=17]

Halt communications script execution when the current query (poll) and response is finished. The script Delay timer is allowed to continue counting down if a Delay script command is in progress.


ComResumeScript [D>=17]

Resume a script that is Stopped or Pausing (otherwise ignored).


ComRunScript pppp [D>=17]

Starts running the control script at NVEM page 0 address pppp.

Typically this is used to invoke a communications script for ModBus. Example


ComSetCCB pppp [D>=17]

Invokes the Communications Control Block at address pppp in NVEM page 0.

Note that SPLat will not switch away from its native SPLatLink protocol in the first 10 seconds after a reset. This is done to make sure you can always gain control of a controller with SPLat/PC.

See also ComTestStartTimer Communication Control Block elements


aComSetCCB pp,nnnn [D>=29]

Applies the Communications Control Block at address nnnn in NVEM page 0 to the serial port at address pp.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Note that SPLat will not switch away from its native SPLatLink protocol in the first 10 seconds after a reset. This is done to make sure you can always gain control of a controller with SPLat/PC.

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.

See also ComTestStartTimer Communication Control Block elements


ComTestStartTimer [D>=17]

Returns True in X for the first10 seconds since reset. During this period an attempt to switch to a nonzero protocol number will stall the processor until the timer has expired. The timer is used to force SPLatLink native protocol as the only protocol during the first 10 seconds, to guarantee it will always be possible to take control of a controller with SPLat/PC.


Com_Flags [D>=23]

Pushes communications status flags into X.

The flags are:

Bit 0 = Rx buffer is full

Bit 1 = Rx buffer is empty

Bit 2 = Tx buffer is full

Bit 3 = Tx buffer is empty

Dialect exclusions: Not available in dialects before 23


aCom_Flags pp [D>=29]

Pushes communications status flags for the serial port at address pp into X.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

The flags are:

Bit 0 = Rx buffer is full

Bit 1 = Rx buffer is empty

Bit 2 = Tx buffer is full

Bit 3 = Tx buffer is empty

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_BufLen [D>=23]

Return character count in Rx buffer to X

Dialect exclusions: Not available in dialects before 23


aComRx_BufLen pp [D>=29]

Return character count in Rx buffer for the serial port at address pp to X

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_DelXChars [D>=23]

Remove the first X characters from the buffer, up to current content. No action if X=0. Pop X

Dialect exclusions: Not available in dialects before 23


aComRx_DelXChars pp [D>=29]

Remove the first X characters from the Rx buffer for the serial port at address pp, up to current content. No action if X=0. Pop X

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_fGetNum fw [D>=26]

Return a decimal number from the head of the Rx buffer. Skip any leading spaces. Process leading + or – sign if present. Terminate on anything thereafter that's not a decimal digit or decimal point, or after processing fw characters in total (including spaces, sign and decimal point). Consume the characters used but not the terminating character. Result in W, NaN if nothing there. R register signals outcome. 0 = OK, 1 = no valid decimal number present.

Notes:

  1. That this instruction will only process characters that have already been received. You should therefore ensure that either a known terminating character, or the required number of characters, have been received before attempting to execute this instruction.
  2. The character count fw includes leading spaces, signs and decimal point.
  3. The only characters removed from the buffer are leading spaces plus the characters that form the number. Hence, if a terminating character is encountered before fw characters are used, less than fw characters will be removed from the buffer.
  4. Conversion will end on the first unexpected character, including a trailing space, a second sign character or a second decimal point. This overrides the field width.
  5. The valid range for fw is 1 to 255. fw = 255 disables character counting.
  6. Anything more than 7 digits of input is meaningless, as the floating point number representation is only accurate to 6 or 7 decimal places. Excess digits will be nevertheless be consumed, up to the limits of fw.

Dialect exclusions: Not available in dialects before 26

See also ComRx_GetHex, ComRx_BufLen, ComRx_FindXInBuf

Examples

The following shows the result of the instruction ComRx_fGetNum 8 for various receive buffer contents. Underscores represent space characters

Receive buffer contents before (Shown as ASCII (printable) characters. | Result returned in W | Receive buffer contents after conversion | Comment
123.456xxxxxxxx | 123.456 | xxxxxxxx | The first x terminates conversion
123.456_xxxxxxxx | 123.456 | _xxxxxxxx | The trailing space terminates conversion. It is not consumed.
____123.456xxxx | 123 | 456xxxx | The 4 leading spaces are counted in the field width. The decimal point "makes it",
-123.456xxxxxxx | -123.456 | xxxxxxx | All good, got it all
_-123.456xxxxxx | -123.45 | 6xxxxxxx | The last digit is excluded, because the field width of 8 terminates conversion. Furthermore, a subsequent conversion (with I as-is) would return 6.
-12.3.456xxxxx | -12.3 | .456xxxxx | The second decimal point terminates conversion. If the same instruction were executed again (with I as-is), it would return 0.456


aComRx_fGetNum pp,fw [D>=29]

Return a decimal number from the head of the Rx buffer for the serial port at address pp. Skip any leading spaces. Process leading + or – sign if present. Terminate on anything thereafter that's not a decimal digit or decimal point, or after processing fw characters in total (including spaces, sign and decimal point). Consume the characters used but not the terminating character. Result in W, NaN if nothing there. R register signals outcome. 0 = OK, 1 = no valid decimal number present.

Notes:

  1. This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.
  2. This instruction will only process characters that have already been received. You should therefore ensure that either a known terminating character, or the required number of characters, have been received before attempting to execute this instruction.
  3. The character count fw includes leading spaces, signs and decimal point.
  4. The only characters removed from the buffer are leading spaces plus the characters that form the number. Hence, if a terminating character is encountered before fw characters are used, less than fw characters will be removed from the buffer.
  5. Conversion will end on the first unexpected character, including a trailing space, a second sign character or a second decimal point. This overrides the field width.
  6. The valid range for fw is 1 to 255. fw = 255 disables character counting.
  7. Anything more than 7 digits of input is meaningless, as the floating point number representation is only accurate to 6 or 7 decimal places. Excess digits will be nevertheless be consumed, up to the limits of fw.

Dialect exclusions: Not available in dialects before 26

Can only exist on boards that have an uncommitted serial port other than the programming port.

See also COMRx_GetHex, COMRx_BufLen, COMRx_FindXInBuf

Examples

The following shows the result of the instruction ComRx_fGetNum 8 for various receive buffer contents.

Receive buffer contents before (Shown as ASCII (printable) characters. | Result returned in W | Receive buffer contents after conversion | Comment
123.456xxxxxxxx | 23.456 | xxxxxxxx |
123.456xxxx | 123 | 456xxxx | Leading spaces are counted in the field width. The decimal point "makes it",
-123.456xxxxxxx | -123.456 | xxxxxxx | All good, got it all
-123.456xxxxxx | -123.45 |  | The last digit is excluded, because the field width of 8 terminates conversion. Furthermore, a subsequent conversion (with I as-is) would return 6.
-12.3.456xxxxx | -12.3 | .456xxxxx | The second decimal point terminates conversion. If the same instruction were executed again (with I as-is), it would return 0.456

Dialect exclusions: Not available in dialects before 29


ComRx_FindXInBuf (X) [D>=23]

Scan the Rx buffer for the first occurrence of the character code in X, pop X, return the buffer position in X. 0 = first position, 255 = not found. Does not modify the buffer.

Dialect exclusions: Not available in dialects before 23


aComRx_FindXInBuf (X) [D>=23]

Scan the Rx buffer for the serial port at address pp for the first occurrence of the character code in X, pop X, return the buffer position in X. 0 = first position, 255 = not found. Does not modify the buffer.

Dialect exclusions: Not available in dialects before 23

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_GetHex [D>=23]

Return a hex number from the head of the Rx buffer. Skip any leading spaces. Terminate thereafter after 2 hex digits or on first non-hex character. Accept upper or lower case. Consume the characters used but not the terminating character. Result in X, 0 if nothing there. R register signals outcome. 0 = OK, 1 = no valid hex number present.

Hex numbers longer than 8 bits can be read in several grabs.

Dialect exclusions: Not available in dialects before 23


aComRx_GetHex pp [D>=29]

Return a hex number from the head of the Rx buffer for the serial port at address pp. Skip any leading spaces. Terminate thereafter after 2 hex digits or on first non-hex character. Accept upper or lower case. Consume the characters used but not the terminating character. Result in X, 0 if nothing there. R register signals outcome. 0 = OK, 1 = no valid hex number present.

Hex numbers longer than 8 bits can be read in several grabs.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Dialect exclusions: Not available in dialects before 29


ComRx_Peek (X) [D>=23]

Push the Xth character in the Rx buffer to X, leaving the character in the buffer. The buffer position starts counting from 0. The R register signals outcome. 0 = OK, 1 = buffer did not contain that many characters.

Dialect exclusions: Not available in dialects before 23


aComRx_Peek pp [D>=29]

Push the Xth character in the Rx buffer for the serial port at address pp to X, leaving the character in the buffer. The buffer position starts counting from 0. The R register signals outcome. 0 = OK, 1 = buffer did not contain that many characters.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_ReadOne [D>=23]

Push the 1st character in the Rx buffer into X, delete it from buffer. R register signals outcome. 0 = OK, 1 = buffer was empty.

Dialect exclusions: Not available in dialects before 23


aComRx_ReadOne pp [D>=29]

Push the 1st character in the Rx buffer for the serial port at address pp into X, delete it from buffer. R register signals outcome. 0 = OK, 1 = buffer was empty.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Dialect exclusions: Not available in dialects before 29

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComRx_Trim (X) [D>=23]

Test if the first character in the Rx buffer is the code in X. If so trim it off. Repeat until mismatch. The result in X is the number of characters that have been trimmed

Dialect exclusions: Not available in dialects before 23


aComRx_Trim pp [D>=29]

Test if the first character in the Rx buffer for the serial port at address pp is the code in X. If so trim it off. Repeat until mismatch. The result in X is the number of characters that have been trimmed.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Dialect exclusions: Not available in dialects before 23

Can only exist on boards that have an uncommitted serial port other than the programming port.


ComTx_Space [D>=23]

Get the number of free bytes in the Tx buffer to X.

Dialect exclusions: Not available in dialects before 23


aComTx_Space pp [D>=29]

Get the number of free bytes in the Tx buffer for the serial port at address pp to X.

This differs from the same instruction without the leading 'a' in that it explicitly nominates a serial port address (pp) rather than the default serial port.

Dialect exclusions: Not available in dialects before 23

Can only exist on boards that have an uncommitted serial port other than the programming port.


iFindXInBuf bb*,nn [D>=28]

Locate the value in X in a buffer held in RAM.

Consider an Xwire receive data block to be nn bytes of RAM starting at address bb. Scan the block, starting at starting at RAM address bb+I (i.e. the starting address is indexed) for the first occurrence of a byte that matches the value in X, if present.

The result is returned in the X register. 0 = first position, 255 = not found. I is not changed.

Dialect exclusions: Not available in dialects before 28


iifGetNum bb#,nn,fw [D>=28]

Extract (Get) a floating point number from a buffer in RAM.

Consider an Xwire receive data block to be nn bytes of RAM starting at address bb. Return a decimal number from the data block, starting at RAM address bb+I (i.e. the starting address is indexed). Skip any leading spaces. Process leading + or – sign, if present. Terminate on anything thereafter that's not a decimal digit or decimal point, or after processing fw (field width) characters in total (including spaces, sign and decimal point). Advance I past the characters used, leaving it pointing at the terminating character (auto-index). Result in W, NaN if nothing there. R register signals outcome. 0 = OK, 1 = no valid decimal number present.

Notes:

  1. Typically used for receiving ASCII-coded information from an Xwire peripheral board.
  2. The field width fw includes leading spaces, signs and decimal point.
  3. Conversion will end on the first unexpected character, including a second sign character, a second decimal point or a trailing space. This overrides the field width.
  4. The valid range for fw is 1 to 255. fw = 255 disables character counting.
  5. Anything more than 7 digits of input is meaningless, as the floating point number representation is only accurate to 6 or 7 decimal places. Excess digits will be nevertheless be consumed, up to the limits of nn and fw.

Dialect exclusions: Not available in dialects before 28

See also: iifPrintWFW, iifPrintWVW,

Examples

The following shows the result of the instruction iifGetNum 100,15,8 for various RAM contents and Values of I.

Data in RAM, starting at address 100 (Shown as ASCII (printable) characters. | Initial value of I | Result returned in W | Final value of I | Comment
123.456xxxxxxxx | 0 | 123.456 | 7 | Conversion starts at the first character, because I = 0. Conversion is terminated by the first x.
123.456xxxxxxxx | 1 | 23.456 | 7 | Conversion starts at the second character, because I = 1.
123.456xxxx | 0 | 123 | 8 | Leading spaces are counted in the field width. The decimal point "makes it", and I gets left pointing at the 4. A subsequent conversion with the same instruction would return 456
-123.456xxxxxxx | 0 | -123.456 | 8 | All good, got it all
-123.456xxxxxx | 0 | -123.45 | 8 | The last digit is excluded, because the field width of 8 terminates conversion. Furthermore, a subsequent conversion would return 6.
-12.3.456xxxxxx | 0 | -12.3 | 6 | The second decimal point terminates conversion. If the same instruction were executed again (with I as-is), it would return 0.456


iiGetHex bb#,nn [D>=28]

Extract (Get) a (8-bit) ASCII coded (printable) hexadecimal number from a buffer in RAM.

Consider an Xwire receive data block to be nn bytes of RAM starting at address bb. Extract a hexadecimal number from the data block, starting at RAM address bb+I (i.e. the starting address is indexed). Skip any leading spaces. Process at most 2 hexadecimal digits. Terminate on anything that's not a hex digit. Advance I past the characters used, leaving it pointing at the terminating character (auto-index). Result in X, 0 if nothing there. R register signals outcome. 0 = OK, 1 = no valid hexadecimal number present. This instruction is not case sensitive.

Notes:

  1. Typically used for receiving ASCII-coded information from an Xwire peripheral board.
  2. Conversion will end on the first unexpected character or after 2 hexadecimal digits.

Dialect exclusions: Not available in dialects before 28

See also: iifPrintWFW, iifPrintWVW,

Example

The following program will write some test data to RAM (iiPrintText) and then loop around extracting hexadecimal numbers from the data. To try the program yourself in SPLat/PC (simulation).:

  1. Launch SPLat/PC.
  2. Set the dialect to 28 (File>Configure>Dialect). If you don't have you will need to upgrade SPLat/PC.
  3. Copy the code below into the edit window.
  4. Display the registers (type CTRL+R)
  5. Set a breakpoint on the last line (position the cursor and press F9)
  6. Reset everything by typing CTRL+F7.
  7. Press F5 repeatedly, and look at the result in X, I and R each time.
  8. Type CTRL+F7 at any time to start again.

XWBuffer        mEQU    100
BufLen          EQU     30

        LoadI   0

        iiPrintText      XWBuffer,"1 2    A   AB DEFedcba9876543210abcdef"

        ;                          012345678901234567890123456789

        LoadI   0

Agin:

        iiGetHex        XWBuffer,BufLen

        GoTo            Agin  ;<<<<<<  Breakpoint here


iiPrintNVText dest# [D>=27]

Print text from NVEM.

The zero-terminated text string currently pointed to by the NVEM Pointer + RecLen x RecNum will be sent to the destination called for in dest. The string can be any series of bytes, but it must end in a 0 byte (and cannot therefore contain a zero byte).

Dialect 27 Firmware failed to apply the RecLen x RecNum offset. This is corrected in dialect 28 firmware.

The maximum number of bytes per message to the serial port is 255. However, the serial transmit buffer is 128 bytes (which could change in future Firmware revisions). If you try to send out more than the buffer can hold, your program will stall until there is room in the buffer (this is your whole program, not just a single MultiTrack task). You can avoid this by using COMTx_Space to ensure there is room before attempting to output.

This instruction allows you to store a text string in NVEM and then invoke it from several places in your program. It can also facilitate creating multi-language applications.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Example string in NVEM

strQBFMessage:      NV0Byte      "The quick brown fox",0

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 27.

See also iiPrintText


iiPrintText dest#,text-string [D >= 23]

Print text.

The text string given in the instruction will be sent to the destination called for in dest.

The maximum number of bytes per message to the serial port is 255. However, the serial transmit buffer is 128 bytes (which could change in future Firmware revisions). If you try to send out more than the buffer can hold, your program will stall until there is room in the buffer (this is your whole program, not just a single MultiTrack task). You can avoid this by using COMTx_Space to ensure there is room before attempting to output.

The text "string" can be a comma-delimited list of bytes in any of the following formats:

Example | Max value | Description
23 | 255 | Decimal constant
'A3 | 255 | Hex constant
%10101100 | 255 | Binary constant
"Hello" |  | A quoted text string

Strings may be any printable characters, enclosed in quotes. A quotation mark is represented by two quotation marks in a row, viz ""

Any number of individual bytes, words and strings may be concatenated, with commas separating them. Spaces are not allowed except within quoted strings.

Examples:

iiPrintText	SerOut,"The quick brown fox",'0D,'0A

will print the quoted message followed by '0D (carriage return) and '0A (line feed).

iiPrintText	XWBuf,'FF,'09,'E3,'67,'FF

would print a series of hexadecimal bytes, perhaps destined for an Xwire peripheral that can make sense of them.

This instruction is automatically indexed.

When printing to RAM the index register I is added to the dest address and the result is stored in that location. I is then incremented by the number of characters stored. This allows several things to be "printed" to RAM.

"Printing" to RAM is typically used for communicating with an Xwire peripheral.

The dest argument determines where the result of the instruction is directed.

Destination address (dest)

Where the result is directed

0 - 249

The result is stored in successive RAM locations, starting at dest. It will occupy as many RAM locations as are needed to contain the result, one location per character. For example, the result of iifPrintWFW 25,7,3 will occupy 7 bytes, RAM locations 25 through 31, because the format specifier calls for 7 digits of output. A runtime error will occur if the output over-runs the end of RAM (address 249). Using RAM as the destination is particularly useful for sending data out on Xwire.

Note: These addresses are not jndexed. Instead they use I as an offset from the dest address, and automatically increment I by the number of bytes stored in RAM by the instruction.

251 - 254

The result is directed to the serial port at the address given, providing the selected protocol is "User programmable", and that the controller you are programming has a serial port at that address. This is the primary means of generating any arbitrary serial output under program control.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 23.

See also iiPrintNVText


iiStrFind bb#,nn,c [D>=28]

Match a string in RAM to a table in NVEM. This is subtly different to iiStrFind2, which uses a different table format.

Consider an Xwire receive data block to be nn bytes of RAM starting at address bb. This instruction seeks to match the data in the block with one or more "canned" strings stored in NVEM. Its primary use is for decoding information that has come in via an Xwire peripheral.

If argument c is zero, the instruction treats upper and lower case letters as equivalent. If c is non-zero, the case of the RAM string must match exactly the string stored in NVEM.

The data in NVEM must be stored in a very specific "NULL terminated" format. This is best illustrated by an example. Lets say we want to detect which fruit has been requested via an Internet interface and transferred into an Xwire receive data block.

1. NVEM: Message list

First you construct a list of all possible receive strings in NVEM. Each string must be terminated with a 0 byte. The 0 is not part of the string, it merely signals the end of the string (C-style NULL terminated strings).

strOrange:      NV0Byte   "Orange",13,0
strApple:       NV0Byte   "Apple",13,0
strBanana:      NV0Byte   "Banana",13,0
strLemon:       NV0Byte   "Lemon",13,0
strPear:        NV0Byte   "Pear",13,0
strGrape:       NV0Byte   "Grape",13,0
;        Terminator _________________^

2. NVEM: Pointer table

Next you construct a table, in NVEM, of pointers to the individual strings. This must be terminated in two 255 bytes. The 255s tell iiStrFind that it has come to the end of the table.

FruitNames:     NV0Ptr    strOrange	;Entry #1
                NV0Ptr    strLemon 	;Entry #2
                NV0Ptr    strApple 	;Entry #3
                NV0Ptr    strPear  	;Entry #4
                NV0Ptr    strBanana	;Entry #5
                NV0Ptr    strGrape 	;Entry #6
                NV0Byte   255,255      ;<<<<< Essential!!!

You will notice that the order of entries is different to the order of the strings themselves. They can be in any order you like. You can have several such tables, with different ordering. You can even use different sub-sets of strings at

3. Code: Wait for a message string

Before trying to extract or recognize a string, you must know that a string has actually been received. How you do this will depend on the particular source of the data that is being stored in RAM. We cannot therefore provide such detail here. (See ComRx_StrFind to see how it's done for serial data). Here we'll just have example code for defining the Rx data block.:

XWRxBlock	defBYTE	30
XWRxBlkLen	EQU		30

4. Code: Find a string match.

This is where we use the actual iiStrFind instruction. First we have to make sure the NVEM access is correctly set up.

                NVSetPtr   FruitNames
                NVSetPage  0           ;0 is the default, so playing very safe!
                LoadI	0		;Start the search at the start of the buffer
                iiStrFind  XWRxBlock,XWRxBlkfLen,0

5. Code: Evaluate the result.

The iiStrFind returns its result in X, as follows:

  • If a match is found, the X-register contains the number of the entry in the pointer table, counting from 1. Thus, if in this case the received string was "Apple",13, X would contain 3.
  • If a match is not found, X will contain 0 (which makes Branch instruction very appropriate).

If a match is found, the index register I is incremented to point to the first character in RAM after the string (auto-index).

                Branch
                Target     NoMatch
                Target     RxOrange
                Target     RxLemon
                Target     RxApple
                Target     RxPear
                Target     RxBanana
                Target     RxGrape

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

You would place your own code to respond to each of the strings at RxOrange, RxLemon, etc.

Quite likely you would then go back to waiting for the next incoming message.

Notes/reminders:

  1. The instruction only moves I past the characters of any string it matches.
  2. The string must start at the position initially pointed to by I.
  3. The 3rd instruction argument controls case sensitivity
  4. Each string in NVEM must end in a 0 (NULL) byte.
  5. The table of pointers must end in two 255 bytes.
  6. The record length and record number pointers are not involved in this instruction. Only the page and pointer matter.
  7. The ComRx_StrFind instruction has very similar behaviour, only it works on data received via the serial port.


iiStrFind2 bb#,nn,c [D>=29]

Match a string in RAM to a table in NVEM. This is subtly different to iiStrFind, which uses a different table format.

Consider an Xwire receive data block to be nn bytes of RAM starting at address bb. This instruction seeks to match the data in the block with one or more "canned" strings stored in NVEM.

If argument c is zero, the instruction treats upper and lower case letters as equivalent. If c is non-zero, the case of the RAM string must match exactly the string stored in NVEM.

The data in NVEM must be stored in a very specific Count/Data format. This is best illustrated by an example. Lets say we want to detect which fruit has been requested via an Internet interface and transferred into an Xwire receive data block.

1. NVEM: Message list

First you construct a list of all possible receive strings in NVEM. Each string must be preceded by a byte containing the length of the string itself.

strOrange:      NV0Byte   7,"Orange",13
strApple:       NV0Byte   6,"Apple",13
strBanana:      NV0Byte   7,"Banana",13
strLemon:       NV0Byte   6,"Lemon",13
strPear:        NV0Byte   5,"Pear",13
strGrape:       NV0Byte   6,"Grape",13
;   Byte count ___________^

2. NVEM: Pointer table

Next you construct a table, in NVEM, of pointers to the individual strings. This must be terminated in two 255 bytes. The 255s tell iiStrFind2 that it has come to the end of the table.

FruitNames:     NV0Ptr    strOrange	;Entry #1
                NV0Ptr    strLemon 	;Entry #2
                NV0Ptr    strApple 	;Entry #3
                NV0Ptr    strPear  	;Entry #4
                NV0Ptr    strBanana	;Entry #5
                NV0Ptr    strGrape 	;Entry #6
                NV0Byte   255,255      ;<<<<< Essential!!!

You will notice that the order of entries is different to the order of the strings themselves. They can be in any order you like. You can have several such tables, with different ordering. You can even use different sub-sets of strings at

3. Code: Wait for a message string

Before trying to extract or recognize a string, you must know that a string has actually been received. How you do this will depend on the particular source of the data that is being stored in RAM. We cannot therefore provide such detail here. (See ComRx_StrFind2 to see how it's done for serial data). Here we'll just have example code for defining the Rx data block.:

XWRxBlock	defBYTE	30
XWRxBlkLen	EQU		30

4. Code: Find a string match.

This is where we use the actual iiStrFind2 instruction. First we have to make sure the NVEM access is correctly set up.

                NVSetPtr    FruitNames
                NVSetPage   0           ;0 is the default, so playing very safe!
                LoadI	 0		 ;Start the search at the start of the buffer
                iiStrFind2  XWRxBlock,XWRxBlkfLen,0

5. Code: Evaluate the result.

The iiStrFind2 returns its result in X, as follows:

  • If a match is found, the X-register contains the number of the entry in the pointer table, counting from 1. Thus, if in this case the received string was "Apple",13, X would contain 3.
  • If a match is not found, X will contain 0 (which makes Branch instruction very appropriate).

If a match is found, the index register I is incremented to point to the first character in RAM after the string (auto-index).

                Branch
                Target     NoMatch
                Target     RxOrange
                Target     RxLemon
                Target     RxApple
                Target     RxPear
                Target     RxBanana
                Target     RxGrape

RxOrange:   ;Do the orange thing ....

RxLemon:    ;Do the lemon thing ....

RxApple:    ;Do the apple thing ....

RxPear:     ;Do the pear thing ....

RxBanana:   ;Do the banana thing ....

RxGrape:    ;Do the grape thing ....

You would place your own code to respond to each of the strings at RxOrange, RxLemon, etc.

Quite likely you would then go back to waiting for the next incoming message.

Notes/reminders:

  1. The instruction only moves I past the characters of any string it matches.
  2. The string must start at the position initially pointed to by I.
  3. The 3rd instruction argument controls case sensitivity
  4. Each string in NVEM must be preceded by a count byte.
  5. The table of pointers must end in two 255 bytes.
  6. The record length and record number pointers are not involved in this instruction. Only the page and pointer matter.
  7. The ComRx_StrFind2 instruction has very similar behaviour, only it works on data received via the serial port.


FastTrack instructions

FastTrack is a small subset of the total SPLat instruction repertoire. It consists of only 14 instructions. With just these 14 instructions it is possible to write many of the simpler applications, such as sequencing a parts press.

Our online training course provides a very thorough grounding in program design and the use of FastTrack. There is an alternative tutorial in the SPLat/PC helpfile.

Please note: The interactive/online training course is very old. Everything in there is valid and worth learning, especially the underlying principles. However the course pre-dates significant developments, most notably our MultiTrack multitasking technology which makes SPLat programming even easier. So, if you are relatively new to controller programming, the course will give you a good foundation, but please don't think it represents the full SPLat story.

In this section we have arranged the instructions in approximate order of ease of understanding rather than alphabetically.

In This Section

On oo*+

Off oo*+

Pause tttt

GoTo LLLL

WaitOn ii+

WaitOff ii+

GoIfInOn ii+,LLLL

GoIfInOff ii+,LLLL

WaitOnT ii+,tttt

WaitOffT ii+,tttt

GoIfT LLLL

GoIfF LLLL

SetMem mm*+,nn

DecMGoIfNZ mm*+,LLLL


On oo*+

(A FastTrack instruction)

Turn on the nominated output number oo. Example:

On    5     ;Turn on the pump

Advanced:

* This instruction can also use the Index Register by adding i in front of the instruction (iOn is not classified as FastTrack).

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See also Off, Output


Off oo*+

(A FastTrack instruction)

Turn off the nominated output number oo

Example:

Off    5     ;Turn off the pump

Advanced:

* This instruction can also use the Index Register by adding i in front of the instruction (iOff is not classified as FastTrack).

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See also On, Output


Pause tttt

(A FastTrack instruction)

Introduces a delay of tttt x 10mS into your program. tttt is given in multiples of 10mS, up to a maximum of 32,767 x 10mS for dialects before 16, and 16,777,215 x 10mS for dialects 16 and later.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller.

From dialect 16 this instruction becomes non-blocking when executed inside a MultiTrack task. When encountered in a MultiTrack task, it will always yield the processor the first time it is executed and continue yielding until the time has expired.

Warning: You cannot use Pause, WaitOnT, WaitOffT or WaitOnKT inside a MarkTime/LoopIfTiming loop.


GoTo LLLL

Transfers program control (jumps) to the program line LLLL

See also GoIfZ, GoIfNZ. Do not confuse with GoSub


WaitOn ii+

(A FastTrack instruction)

Waits until the nominated input number ii turns on. If the input is already on when this instruction is executed, your program will simply go on to the next instruction. If the input is off, the program will wait until it turns on, with no time limit. You should therefore take care when using this instruction not to produce a situation where your program can "hang up" because the expected input never occurs. In a MultiTrack task this is not a problem.

This instruction uses debounced inputs. Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches.

When this instruction is simulated the regular timers will count at an arbitrary rate.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

See also WaitOnT WaitOff WaitOffT WaitOnK WaitOnKT


WaitOff ii+

(A FastTrack instruction)

Waits until the nominated input number ii turns off. If the input is already off when this instruction is executed, your program will simply go on to the next instruction. If the input is on, the program will wait until it turns off, with no time limit. You should therefore take care when using this instruction not to produce a situation where your program can "hang up" because the expected input never occurs. In a MultiTrack task this is not a problem.

This instruction uses debounced inputs. Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches.

When this instruction is simulated the regular timers will count at an arbitrary rate.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

See also WaitOn WaitOnT WaitOffT WaitOnK WaitOnKT


GoIfInOn ii+,LLLL

(A FastTrack instruction)

This instruction tests the nominated input i. If the input is on, the program will go to (jump to) the program line with the label LLLL. If the input is not on, the program will continue at the next instruction.

This instruction uses debounced inputs. Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

See also GoIfInOff


GoIfInOff ii+,LLLL

(A FastTrack instruction)

This instruction tests the nominated input ii. If the input is off, the program will go to (jump to) the program line with the label LLLL. If the input is on, the program will continue at the next instruction.

This instruction uses debounced inputs. Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

See also GoIfInOn


WaitOnT ii+,tttt

(A FastTrack instruction)

Waits until the nominated input number ii turns on, but with a time limit. If the input is already on when this instruction is executed, your program will simply go on to the next instruction. If the input is off, the program will wait until it turns on, but with a maximum wait time defined by the argument tttt. tttt is given in multiples of 10mS, up to a maximum of 32,767 x 10mS for dialects before 16, and 16,777,215 x 10mS for dialects 16 and later.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller.

If the expected input occurs within the allowable time limit, the instruction Pushes a True value into X. If on the other hand the timeout occurred, the instruction Pushes a False value into X. A simple conditional GoTo (GoIfT or GoIfF) can test the outcome. (This all becomes clear in the FastTrack tutorial).

From dialect 16 this instruction becomes non-blocking when executed inside a MultiTrack task. When encountered in a MultiTrack task, it will always yield the processor the first time it is executed and continue yielding until the time has expired or the input condition is met.

This instruction uses debounced inputs. Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches.

When this instruction is simulated it has a special time display. Also, the simulated timing of the regular timers is unrelated to the timing of this instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Warning: You cannot use Pause, WaitOnT, WaitOffT or WaitOnKT inside a MarkTime/LoopIfTiming loop.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

See also WaitOn WaitOff WaitOffT WaitOnK WaitOnKT Pause


WaitOffT ii+,tttt

(A FastTrack instruction)

Waits until the nominated input number ii turns off, but with a time limit. If the input is already off when this instruction is executed, your program will simply go on to the next instruction. If the input is on, the program will wait until it turns off, but with a maximum wait time defined by the argument tttt. tttt is given in multiples of 10mS, up to a maximum of 32,767 x 10mS for dialects before 16, and 16,777,215 x 10mS for dialects 16 and later.

From dialect 16 this instruction becomes non-blocking when executed inside a MultiTrack task. When encountered in a MultiTrack task, it will always yield the processor the first time it is executed and continue yielding until the time has expired or the input condition is met.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller.

If the expected input occurs within the allowable time limit, the instruction Pushes a True value into X. If on the other hand the timeout occurred, the instruction Pushes a False value into X. A simple conditional GoTo (GoIfT or GoIfF) can test the outcome. (This all becomes clear in the FastTrack tutorial).

This instruction uses debounced inputs. Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches.

When this instruction is simulated it has a special time display. Also, the simulated timing of the regular timers is unrelated to the timing of this instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack

Warning: You cannot use Pause, WaitOnT, WaitOffT or WaitOnKT inside a MarkTime/LoopIfTiming loop.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

See also WaitOn WaitOnT WaitOff WaitOnK WaitOnKT Pause


GoIfT LLLL

The GoIfT instruction will go to the program line LLLL if the previous operation gave a TRUE result.

This is an alias for (does exactly the same as) GoIfNZ intended for FastTrack users.


GoIfF LLLL

The GoIfF instruction will go to the program line LLLL if the previous operation gave a FALSE result.

This is an alias for (does exactly the same as) GoIfZ intended for FastTrack users.


SetMem mm*+,nn

(A FastTrack instruction)

Sets the data memory location mm to contain the number 'nn'

mm must be a valid data memory address (usually 0 through 63). 'nn' must be in the range 0 to 255.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7, but can be used on older boards via automatic translation by SPLat/PC.

Indexing: From dialect 15 this instruction can be indexed by adding 'i' in front of it.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See also DecMGIfNZ


Floating point instructions

Floating point arithmetic lets you perform arithmetic calculations in SPLat as easily as writing down a series of operations on a desk calculator.

In This Section

fAbs

fAdd

fAnIn c+ [D>=18]

fAnOut c+ [D>=18]

fCompareR

fDec

fDiv

fGetTimer t

fGoIfNeg LLLL

fGoIfNZ LLLL

fGoIfPos LLLL

fGoIfWGEQ LLLL [D>=23]

fGoIfWGTQ LLLL [D>=23]

fGoIfWLEQ LLLL [D>=23]

fGoIfWLTQ LLLL [D>=23]

fGoIfZ LLLL

fIEEEtoW [D>=21]

fIEEEtoWR [D>=21]

fInc

fix

FixToMem16S mm* [D>=21]

FixToMem16U mm* [D>=21]

fLoadQ ffff

fLoadW ffff

float

FloatMem16S mm* [D>=21]

FloatMem16U mm* [D>=21]

fMul

fNeg

iifPrintWFW dest#,f,p [D>=23]

iifPrintWVW dest#,f,p [D>=23]

fPulse0 [D>=19]

fRecallQ mm*+

fRecallW mm*+

fSetTimer t

fSign

fStore mm*+

fSTSinceMark [D>=22]

fSTTimeSince aa+ [D>=16]

fSub

fSwap

fTest

fTestWeqQ [D>=24]

fTestWeqZ [D>=25]

fTestWgeQ [D>=25]

fTestWgeZ [D>=25]

fTestWgtQ [D>=25]

fTestWleQ [D>=25]

fTestWltQ [D>=25]

fTestWltZ [D>=25]

fTestWneQ [D>=25]

fTestWneZ [D>=25]

fWtoQ

fWtoIEEE [D>=21]

fWtoIEEER [D>=21]

NVfGetEndW [D>=15]

NVfGetPtrW [D>=15]

NVfGetRecNumW [D>=15]

NVfPutPtrW [D>=15]

NVfPutRecNumW [D>=15]

NVfReadQ nn*+ [D>=15]

NVfReadW nn*+ [D>=15]

NVfWriteW nn*+ [D>=15]

OBCA_fRead

OBLCD_fDispW f,p

OBCA_fRdClr

OBCA_fRead

Result codes for fAdd, fSub, fMul, fDiv

Result codes for fCompareR

Result codes for fInc, fDec

Result codes for fix

Result codes for fSetTimer

Result codes for fTest


fAbs

Find the absolute value of a floating point number.

Forces W to be positive. It works on all numbers including infinity. NaN is unaffected.

The R-Register is not affected.

Dialect restriction: The fAbs instruction is not implemented in boards with dialect numbers less than 6, or which do not have the floating point processor.

See also fSign, fNeg


fAdd

Adds W to Q and leaves the result in W. Q is unchanged.

Click here for Result Codes.

Dialect restriction: The fAdd instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fSub, fMul, fDiv


fCompareR

Compares the numbers W and Q and leaves the result in R. W and Q are unchanged.

Click here for Result Codes.

Dialect restriction: The fCompareR instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fTest, Compare, CompareR BranchR


fDiv

Divides W into Q and leaves the result in W, in other words W=Q/W. Q is unchanged.

Click here for Result Codes.

Dialect restriction: The fDiv instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fAdd, fSub, fMul


fGetTimer t

Retrieves the current value in timer t as a floating point value in W. This is the complementary instruction to fSetTimer, and allows you to readily measure time intervals and base calculations on the result.

Dialect exclusions: Not available in dialects before 9.


fGoIfNeg LLLL

Tests the contents of the floating point register W, transfers program control (jumps) to the destination LLLL if W<0 (including -Infinity). Will not jump if W=NaN. Does not affect the contents of W.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fGoIfNZ, fGoIfPos, fGoIfZ, fGoIfWLTQ, fGoIfWLEQ, fGoIfWGTQ


fGoIfNZ LLLL

Tests the contents of the floating point register W, transfers program control (jumps) to the destination LLLL if W<>0 (including Infinity). Will not jump if W=NaN. Does not affect the contents of W.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fGoIfNeg, fGoIfPos, fGoIfZ, fGoIfWLTQ, fGoIfWLEQ, fGoIfWGTQ

Note that due to the accuracy limitations of floating point arithmetic, a number you expect to be zero may in fact have a very small non-zero value, and will test as non-zero.


fGoIfPos LLLL

Tests the contents of the floating point register W, transfers program control (jumps) to the destination LLLL if W>0 (including +Infinity). Will not jump if W=NaN. Does not affect the contents of W.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fGoIfNeg, fGoIfNZ, fGoIfZ, fGoIfWLTQ, fGoIfWLEQ, fGoIfWGTQ


fGoIfWGEQ LLLL [D>=23]

"Go if W greater than or equal to Q"

Compares the contents of the floating point registers W and Q. Transfers program control (jumps) to the destination LLLL if W greater than or equal to Q. Does not affect the contents of W or Q.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 23.

See also fGoIfWLTQ, fGoIfWLEQ, fGoIfWGTQ

Note that there is an inherent hazard in comparing two floating point numbers for equality. The numbers you are comparing may have been arrived at via a number of arithmetic operations. Because floating point arithmetic has limited accuracy (24 bits = about 6 or 7 decimal digits in our case), two numbers that should be identical may differ by a small amount. That means an equals test may fail on two numbers that should be identical.


fGoIfWGTQ LLLL [D>=23]

"Go if W greater than Q"

Compares the contents of the floating point registers W and Q. Transfers program control (jumps) to the destination LLLL if W greater than Q. Does not affect the contents of W or Q.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 23.

See also fGoIfWLTQ, fGoIfWLEQ, fGoIfWGEQ

Note that there is an inherent hazard in comparing two floating point numbers for equality. The numbers you are comparing may have been arrived at via a number of arithmetic operations. Because floating point arithmetic has limited accuracy (24 bits = about 6 or 7 decimal digits in our case), two numbers that should be identical may differ by a small amount. That means an equals test may fail on two numbers that should be identical.


fGoIfWLEQ LLLL [D>=23]

"Go if W less than or equal to Q"

Compares the contents of the floating point registers W and Q. Transfers program control (jumps) to the destination LLLL if W is less than or equal to Q. Does not affect the contents of W or Q.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 23.

See also fGoIfWLTQ, fGoIfWGTQ, fGoIfWGEQ

Example:

    fAnin		TempSensor		;Read the temperature sensor
    fRecallQ	SetPoint		;Get the current set point
    fGoIfWLEQ	TurnOnHeater	;Jump if the temperature is too low

Note that there is an inherent hazard in comparing two floating point numbers for equality. The numbers you are comparing may have been arrived at via a number of arithmetic operations. Because floating point arithmetic has limited accuracy (24 bits = about 6 or 7 decimal digits in our case), two numbers that should be identical may differ by a small amount. That means an equals test may fail on two numbers that should be identical.


fGoIfWLTQ LLLL [D>=23]

"Go if W less than Q"

Compares the contents of the floating point registers W and Q. Transfers program control (jumps) to the destination LLLL if is less than Q. Does not affect the contents of W or Q.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 23.

See also fGoIfWLEQ, fGoIfWGTQ, fGoIfWGEQ

Example:

    fAnin		TempSensor		;Read the temperature sensor
    fRecallQ	SetPoint		;Get the current set point
    fGoIfWLTQ	TurnOnHeater	;Jump if the temperature is too low

Note that there is an inherent hazard in comparing two floating point numbers for equality. The numbers you are comparing may have been arrived at via a number of arithmetic operations. Because floating point arithmetic has limited accuracy (24 bits = about 6 or 7 decimal digits in our case), two numbers that should be identical may differ by a small amount. That means an equals test may fail on two numbers that should be identical.


fGoIfZ LLLL

Tests the contents of the floating point register W, transfers program control (jumps) to the destination LLLL if W=0. Will not jump if W=NaN. Does not affect the contents of W.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fGoIfNeg, fGoIfNZ, fGoIfPos, fGoIfWLTQ, fGoIfWLEQ, fGoIfWGTQ

Note that due to the accuracy limitations of floating point arithmetic, a number you expect to be zero may in fact have a very small non-zero value, and will test as non-zero.


fIEEEtoW [D>=21]

Converts the data in W from IEEE754 floating point format to SPLat floating point format. This is used when transferring floating point data from a 3rd party device, say an HMI, to the SPLat.

Explanation: The floating point format adopted in SPLat is a special format designed to be more efficient for implementation in an 8-bit processor. It keeps mantissa and exponent in separate bytes, whereas in IEEE they cross byte boundaries.

A typical use scenario would be that you are using ModBus to transfer a floating point number from an external 3rd party HMI to the SPLat. The number winds up in 4 bytes of SPLat RAM, but is in IEEE format. To convert to SPLat format for further use you get it into W using fRecallW then you use fIEEEtoW.

Dialect restriction: Available only in boards with dialect 21 or greater.

See also fIEEEtoWR, fWtoIEEE, fWtoIEEER, FixToMem16U, FixToMem16S, FloatMem16U, FloatMem16S


fIEEEtoWR [D>=21]

Converts the data in W from IEEE754 floating point format to SPLat floating point format, with the word order reversed. This is used when transferring floating point data from a 3rd party device, say an HMI, to the SPLat. The word order reversal swaps the first two bytes with the 2nd two bytes. This is done to provide compatibility with certain 3rd party devices that use reversed word order. In general, different companies handle floating point formats in different ways, so compatibility can be elusive.

Explanation: The floating point format adopted in SPLat is a special format designed to be more efficient for implementation in an 8-bit processor. It keeps mantissa and exponent in separate bytes, whereas in IEEE they cross byte boundaries.

A typical use scenario would be that you are using ModBus to transfer a floating point number from an external 3rd party HMI to the SPLat. The number winds up in 4 bytes of SPLat RAM, but is in IEEE format. To swap word order and convert to SPLat format for further use you get it into W using fRecallW then you use fIEEEtoWR.

Dialect restriction: Available only in boards with dialect 21 or greater.

See also fIEEEtoW, fWtoIEEE, fWtoIEEER, FixToMem16U, FixToMem16S, FloatMem16U, FloatMem16S


fix

(Convert from Floating Point to fixed point (integer))

Converts the number in floating point register W to an integer nn, Pushes the integer result to X and sets a Return Code in R.

The resulting number in X, and the result code in R, depend on the number in W

W | X | R
0 <= W < 256 | Integer part of W | 0
-256 < W < 0 | Integer part of -W | 1
256 <= W <= +Inf | 255 | 2
-Inf <= W <= -256 | 255 | 3
NaN | 0 | 4

Note that in the normal range 0 to 255 (and down to -255), the fix instruction truncates the argument. Thus 13.999 (and -13.999) gives the result 13.

Dialect restriction: The fix instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also float


FixToMem16S mm* [D>=21]

Converts a floating point number in W, in the range -32,768 to +32,767, to a signed 16-bit integer in two bytes of RAM. The byte order in RAM is most significant byte in the lowest RAM address.

If W is outside the range -32,768 to +32,767 this will generate a fatal error and reset the controller.

Dialect restriction 1: Available only in boards with dialect 21 or greater.

Dialect restriction 2: jndexes/indexes correctly in dialect 22 or greater. No indexing/jndexing in dialect 21.

See also fIEEEtoW, fIEEEtoWR, fWtoIEEE, fWtoIEEER, FixToMem16U, FloatMem16U, FloatMem16S


FixToMem16U mm* [D>=21]

Converts a floating point number in W, in the range 0 to 65,535, to an unsigned 16-bit integer in two bytes of RAM. The byte order in RAM is most significant byte in the lowest RAM address.

If W is outside the range 0 to 65,535 this will generate a fatal error and reset the controller.

Dialect restriction 1: Available only in boards with dialect 21 or greater.

Dialect restriction 2: jndexes/indexes correctly in dialect 22 or greater. No indexing/jndexing in dialect 21.

See also fIEEEtoW, fIEEEtoWR, fWtoIEEE, fWtoIEEER, FixToMem16S, FloatMem16U, FloatMem16S


fLoadQ ffff

Loads a constant number into Q. ffff can be any legal floating point number, such as 3.1415926. The R-register is not affected.

Dialect restriction: The fLoadQ instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fLoadW, fRecallW, fRecallQ, fStore, fSwap, fWtoQ


fLoadW ffff

Loads a constant number into W. ffff can be any legal floating point number, such as 3.1415926. The R-register is not affected.

Dialect restriction: The fLoadW instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fLoadQ, fRecallW, fRecallQ, fStore, fSwap, fWtoQ


float

Convert the integer number in X to its floating point equivalent in W, and Pop register stack.

Dialect restriction: The Float instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fix


FloatMem16S mm* [D>=21]

Converts a 16-bit signed integer value held in RAM to a floating point number in W. The byte order in RAM is most significant byte in the lowest RAM address. The number is treated as signed, so the resulting number range is -32,768 to +32,767

Dialect restriction 1: Available only in boards with dialect 21 or greater.

Dialect restriction 2: jndexes/indexes correctly in dialect 22 or greater. No indexing/jndexing in dialect 21.

See also fIEEEtoW, fIEEEtoWR, fWtoIEEE, fWtoIEEER, FixToMem16U, FixToMem16S, FloatMem16U


FloatMem16U mm* [D>=21]

Converts a 16-bit unsigned integer value held in RAM to a floating point number in W. The byte order in RAM is most significant byte in the lowest RAM address. The number is treated as unsigned, so the resulting number range is 0 to 65,535

Dialect restriction 1: Available only in boards with dialect 21 or greater.

Dialect restriction 2: jndexes/indexes correctly in dialect 22 or greater. No indexing/jndexing in dialect 21.

See also fIEEEtoW, fIEEEtoWR, fWtoIEEE, fWtoIEEER, FixToMem16U, FixToMem16S, FloatMem16S


fMul

Multiplies W and Q and leaves the result in W. Q is unchanged.

Click here for Result Codes.

Dialect restriction: The fMul instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fAdd, fSub, fDiv


fNeg

Replaces the number in floating point register W with its negative.

NaN in W remains unchanged.

The R-register is not affected.

Dialect restriction: The fNeg instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fAbs, fSign


fPulse0 [D>=19]

This instruction generates a precision pulse on output 0. The time duration is the integer value of W, in milliseconds. The range is 8,388,608mS (2.3 hours).

The output generated by this instruction cannot be read back by InputO or InputOM instructions.

Dialect exclusions: Not available in dialects before 19.


fRecallQ mm*+

Recalls a floating point number from data memory location mm through mm+3 and loads it into Q.

The R-Register is not affected.

* This instruction can also use the Index Register by adding i in front of the instruction. For this instruction the value of the index register gets multiplied by 4. That means it can easily be used to index into an array of floating point numbers.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Special note: Firmware versions up to V3.2 (MMi99) and V3.4 (SL99) handled indexed floating point instructions incorrectly.

Dialect restriction: The fRecallQ instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fLoadW, fLoadQ fLoadQ, fRecallW, fStore, fSwap, fWtoQ


fRecallW mm*+

Recalls a floating point number from data memory and loads it into W. The memory location is given in the instruction; the specified location mm plus the following 3 (for a total of 4) are used.

The R-register is not affected.

* This instruction can also use the Index Register by adding i in front of the instruction. For this instruction the value of the index register gets multiplied by 4. That means it can easily be used to index into an array of floating point numbers.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Special note: Firmware versions up to V3.2 (MMi99) and V3.4 (SL99) handled indexed floating point instructions incorrectly.

Dialect restriction: The fRecallW instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fLoadW, fLoadQ, fRecallQ, fStore, fSwap, fWtoQ


fSetTimer t

Sets the nominated "classic" timer to the integer value of the floating point register W. Also, returns a Result Code in R. This is the complementary instruction to fGetTimer. Don't confuse with the newer SuperTimer (D>=16) instructions.

For values of 0 <= W <= 32,767 the timer is set to the next smallest integer <=W (i.e. W is truncated, not rounded).

For values >32,767 the timer is set to 32,767.

For all other values the timer is set to 0.

If there is any chance that W could have a bad value you should check the R register after doing the fSetTimer.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also SetTimer, Test, fGetTimer


fSign

Replaces the number in floating point register W with a number representing its sign.

All positive W, including +Infinity, result in +1 in W.

Zero W results in 0 (i.e. no change)

All negative W, including -Infinity, result in -1 in W.

NaN in W remains unchanged.

The R register is not affected.

Dialect restriction: The fSign instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fAbs, fNeg


fStore mm*+

Stores the FP number in W into data memory in the location mm plus the following 3 locations (using a total of 4 locations)

The R-register is not affected.

* This instruction can also use the Index Register by adding i in front of the instruction. For this instruction the value of the index register gets multiplied by 4. That means it can easily be used to index into an array of floating point numbers.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Special note: Firmware versions up to V3.2 (MMi99) and V3.4 (SL99) handled indexed floating point instructions incorrectly.

Dialect restriction: The fStore instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fLoadW, fLoadQ, fRecallW, fRecallQ, fSwap, fWtoQ


fSTSinceMark [D>=22]

Calculates the time elapsed since the last MarkTime in the current task. The result is returned as a floating point number in W. This will be an integer value in the range 0 to 16,777,215 times 10mS. This instruction may only be used a MultiTrack task. It is based on the assumption that there was a prior MarkTime instruction in this task (failing which the result is unpredictable).

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller.

Warning: You cannot use Pause, WaitOnT, WaitOffT or WaitOnKT between a MarkTime and its corresponding fSTSinceMark.

Explanation: In MultiTrack there is exactly one timer per task that is provided "free" by the system. It is used/shared by Pause, WaitOnT, WaitOffT, WaitOnKT and MarkTime.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 22.

Example:

The following program has two tasks. Timer measures the duration of an "On press" of the push button on input 16 of an MS120 and displays it on the LCD. Blinker blinks the LED on output 11 once per second as long as the button is depressed.

        On              20              ;Backlight
        LaunchTask      Timer
        LaunchTask      Blinker
        RunTasksForever

;Measure and display button on-time
Timer:
        WaitOn          16
        MarkTime                        ;Start timing
        WaitOff         16
        fSTSinceMark                    ;Get the elapsed time
        fLoadQ          10
        fMul
        OBLCD_SetCur    0,0
        OBLCD_fDispW    8,0
        OBLCD_Text      "mS"
        GoTo            Timer

;Blink a LED while the button is on
Blinker:
        Off             11
        WaitOn          16
Blinker2:
        On              11
        WaitOffT        16,5
        GoIfT           Blinker
        Off             11
        WaitOffT        16,95
        GoIfT           Blinker
        GoTo            Blinker2


fSTTimeSince aa+ [D>=16]

Calculates the difference between the current System Time and the value stored in 3 bytes of RAM at address aa. The result is the elapsed time since the System Time was captured to aa, and is returned as a floating point number in W. This will be an integer value in the range 0 to 16,777,215 times 10mS. This instruction is not confined to use in a MultiTrack task. It is based on the assumption that there was a prior STStart instruction.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller.

The address argument aa will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


fSub

Subtracts W from Q and leaves the result in W, in other words W=Q-W. Q is unchanged.

Click here for Result Codes.

Dialect restriction: The fSub instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fAdd, fMul, fDiv


fSwap

Swaps the contents of W and Q

The R-Register is not affected.

Dialect restriction: The fSwap instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fLoadW, fLoadQ, fRecallW, fRecallQ, fStore, fWtoQ


fTest

Tests the number in W and leaves the result in R. W is unchanged.

Click here for Result Codes.

Dialect restriction: The fTest instruction is not implemented in boards with dialect numbers less than 8, or which do not have the FP processor. (fTest was initially implemented in dialects 6 and 7, but did not work correctly.)

See also fCompareR BranchR


fTestWeqQ [D>=24]

Push a TRUE (255) into X if W = Q (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqZ fTestWgeQ fTestWgeZ fTestWgtQ fTestWleQ fTestWltQ fTestWltZ fTestWneQ fTestWneZ

Note that there is an inherent hazard in comparing two floating point numbers for equality. The numbers you are comparing may have been arrived at via a number of arithmetic operations. Because floating point arithmetic has limited accuracy (24 bits = about 6 or 7 decimal digits in our case), two numbers that should be identical may differ by a small amount. That means an equals test may fail on two numbers that should be identical.


fTestWeqZ [D>=25]

Push a TRUE (255) into X if W = 0 (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWgeQ fTestWgeZ fTestWgtQ fTestWleQ fTestWltQ fTestWltZ fTestWneQ fTestWneZ

Note that there is an inherent hazard in comparing two floating point numbers for equality. The numbers you are comparing may have been arrived at via a number of arithmetic operations. Because floating point arithmetic has limited accuracy (24 bits = about 6 or 7 decimal digits in our case), two numbers that should be identical may differ by a small amount. That means an equals test may fail on two numbers that should be identical.


fTestWgeQ [D>=25]

Push a TRUE (255) into X if W >= Q (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeZ fTestWgtQ fTestWleQ fTestWltQ fTestWltZ fTestWneQ fTestWneZ


fTestWgeZ [D>=25]

Push a TRUE (255) into X if W is zero or positive (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeQ fTestWgtQ fTestWleQ fTestWltQ fTestWltZ fTestWneQ fTestWneZ


fTestWgtQ [D>=25]

Push a TRUE (255) into X if W > Q (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeQ fTestWgeZ fTestWleQ fTestWltQ fTestWltZ fTestWneQ fTestWneZ


fTestWleQ [D>=25]

Push a TRUE (255) into X if W <= Q (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeQ fTestWgeZ fTestWgtQ fTestWltQ fTestWltZ fTestWneQ fTestWneZ


fTestWltQ [D>=25]

Push a TRUE (255) into X if W < Q (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeQ fTestWgeZ fTestWgtQ fTestWleQ fTestWltZ fTestWneQ fTestWneZ


fTestWltZ [D>=25]

Push a TRUE (255) into X if W < 0 (i.e. negative) (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeQ fTestWgeZ fTestWgtQ fTestWleQ fTestWltQ fTestWneQ fTestWneZ


fTestWneQ [D>=25]

Push a TRUE (255) into X if W not equal to Q (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeQ fTestWgeZ fTestWgtQ fTestWleQ fTestWltQ fTestWltZ fTestWneZ

Note that there is an inherent hazard in comparing two floating point numbers for equality. The numbers you are comparing may have been arrived at via a number of arithmetic operations. Because floating point arithmetic has limited accuracy (24 bits = about 6 or 7 decimal digits in our case), two numbers that should be identical may differ by a small amount. That means an equals test may fail on two numbers that should be identical.


fTestWneZ [D>=25]

Push a TRUE (255) into X if W is not zero (floating point comparison). Otherwise push 0 (FALSE) into X.

This instruction is designed to assist in the use of Tabula, our table based programming tool.

Dialect restriction: This instruction is not implemented in boards with dialect numbers less than 25.

See also fTestWeqQ fTestWeqZ fTestWgeQ fTestWgeZ fTestWgtQ fTestWleQ fTestWltQ fTestWltZ fTestWneQ


fWtoQ

Copies the contents of W into Q. W is unchanged.

The R-Register is not affected.

Dialect restriction: The fLoadQ instruction is not implemented in boards with dialect numbers less than 6, or which do not have the FP processor.

See also fLoadW, fLoadQ, fRecallW, fRecallQ, fStore, fSwap


fWtoIEEE [D>=21]

Converts the contents of W from SPLat floating point format to IEEE-754 format. This involves re-arranging the byte and bit order to make the number compatible with 3rd party equipment such as graphics HMIs. After this instruction has been executed the data in W should NOT be operated on with any floating point calculation.

This instruction is not accurately simulated by SPLat/PC. In simulation W is set to NaN (Not a Number). That means you cannot use it to explore the two floating point formats.

Explanation: The floating point format adopted in SPLat is a special format designed to be more efficient for implementation in an 8-bit processor. It keeps mantissa and exponent in separate bytes, whereas in IEEE they cross byte boundaries.

Dialect restriction: Available only in boards with dialect 21 or greater.

See also fIEEEtoW, fIEEEtoWR, fWtoIEEER, FixToMem16U, FixToMem16S, FloatMem16U, FloatMem16S


fWtoIEEER [D>=21]

Converts the contents of W from SPLat floating point format to IEEE-754 format with reversed word order. This involves re-arranging the byte and bit order to make the number compatible with 3rd party equipment such as graphics HMIs.

The word order reversal swaps the first two bytes with the 2nd two bytes. This is done to provide compatibility with certain 3rd party devices that use reversed word order. In general, different companies handle floating point formats in different ways, so compatibility can be elusive.

After this instruction has been executed the data in W should NOT be operated on with any floating point calculation.

This instruction is not accurately simulated by SPLat/PC. In simulation W is set to NaN (Not a Number). That means you cannot use it to explore the various floating point formats.

Explanation: The floating point format adopted in SPLat is a special format designed to be more efficient for implementation in an 8-bit processor. It keeps mantissa and exponent in separate bytes, whereas in IEEE they cross byte boundaries.

Dialect restriction: Available only in boards with dialect 21 or greater.

See also fIEEEtoW, fIEEEtoWR, fWtoIEEE, FixToMem16U, FixToMem16S, FloatMem16U, FloatMem16S


NVfGetEndW [D>=15]

Load the last valid address of the page (= page size –1) into W as a float.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVfGetPtrW [D>=15]

Copy the NVEM pointer to W (with conversion to floating point).

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVfGetRecNumW [D>=15]

Copy the RecNum register to W (converting to float).

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVfPutPtrW [D>=15]

Set the NVEM pointer to the value in W. Raises a fatal runtime error if W exceeds the valid range of the pointer register (which may be more than the number of bytes in the page) or is NaN (not a number).

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVfPutRecNumW [D>=15]

Copy the integer part of the number in W to the RecNum register. Raise a fatal error if the value exceeds the capacity of the RecNum register.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVfReadQ nn*+ [D>=15]

Read 4 bytes from NVEM at Pointer + RecLen x RecNum + nn and write to Q. Raise a fatal error if the transfer runs past the end of NVEM.

This instruction can also be used in index mode by using the keyword iNVfReadQ. In that case the transfer is at NVEM address Pointer + RecLen x RecNum + nn + 4ii, where ii is the contents of the index register I.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVfReadW nn*+ [D>=15]

Read 4 bytes from NVEM at Pointer + RecLen x RecNum + nn and write to W. Raise a fatal error if the transfer runs past the end of NVEM.

This instruction can also be used in index mode by using the keyword iNVfReadW. In that case the transfer is at NVEM address Pointer + RecLen x RecNum + nn + 4ii, where ii is the contents of the index register I.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVfWriteW nn*+ [D>=15]

Write W as 4 bytes into NVEM at Pointer + RecLen x RecNum + nn. Raise a fatal error if the transfer runs past the end of NVEM.

This instruction can also be used in index mode by using the keyword iNVfWriteW. In that case the transfer is at NVEM address Pointer + RecLen x RecNum + nn + 4ii, where ii is the contents of the index register I.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.

Note: NVEM has significant limits on how many write cycles it can withstand. Please refer to the documentation for your particular controller to determine what the limitations are. Too many writes are likely to to permanently damage the controller and void the warranty.


Result codes for fAdd, fSub, fMul, fDiv

W | R
Normal number | 0
NaN | 1
±Infinity | 2

These results can be tested using BranchR


Result codes for fCompareR

W vs Q | R
W=Q | 0
W>Q | 1
W<Q | 2
Indeterminate | 3

Result is indeterminate if either W or Q is NaN or if they are both infinity with the same sign.

These results can be tested using BranchR


Result codes for fInc, fDec

W (after) | R
0 | 0
>0 | 1
<0 | 2
NaN | 3
±16,777,215 | 4

These results can be tested using BranchR


Result codes for fix

W | R
0 <= W < 256 | 0
-256 < W < 0 | 1
256 <=W <=+Inf | 2
-Inf <=W < -256 | 3
NaN | 4

These results can be tested using BranchR


Result codes for fSetTimer

W | R | Comment
0 <=W<=32,767 | 0 | Normal
W>32,767 | 1 | Incl +Inf. Timer set to 32,767
W<0 | 2 | Incl -Inf. Timer set to 0
NaN | 3 | Timer set to 0

These results can be tested using BranchR


Result codes for fTest

W | R
0 | 0
>0 | 1
<0 | 2
NaN | 3
±Infinity | 4

These results can be tested using BranchR


GoTo, GoSub and related instructions

There are a number of instructions for making program flow break off and jump to another part of your program. Nearly all of them are conditional, meaning you can test some condition and jump one way or another depending on the outcome.

There is also a rich set of conditional subroutine call (GoSub) and Return instructions. A GoSub instruction differs from a GoTo in that it saves away the address that it comes from, so it is possible to later do a Return and get back again. Subroutines can be nested to a depth that depends on the language dialect, meaning there is a limit to how far you can go having one subroutine call another, because there is a limit to how much memory is dedicated to remembering return addresses.

In This Section

Branch

BranchJ [D>=16]

BranchM mm+

BranchR

DecMGoIfNZ mm*+,LLLL

DecMRetIfNZ mm+

DecMRetIfZ mm+

fGoIfNeg LLLL

fGoIfNZ LLLL

fGoIfPos LLLL

fGoIfWGEQ LLLL [D>=23]

fGoIfWGTQ LLLL [D>=23]

fGoIfWLEQ LLLL [D>=23]

fGoIfWLTQ LLLL [D>=23]

fGoIfZ LLLL

GoIfF LLLL

GoIfInK ii+,LLLL

GoIfInOff ii+,LLLL

GoIfInOn ii+,LLLL

GoIfINZ LLLL

GoIfIZ LLLL

GoIfMEQ mm+,nn,LLLL

GoIfMGE mm+,nn,LLLL

GoIfMGT mm+,nn,LLLL

GoIfMLE mm+,nn,LLLL

GoIfMLT mm+,nn,LLLL

GoIfMNE mm+,nn,LLLL

GoIfMNZ mm+,LLLL

GoIfMZ mm+,LLLL

GoIfNZ LLLL

GoIfRTCEvent e+,LLLL [D>=16]

GoIfSF ss,bb+,LLLL [D>=12]

GoIfSim LLLL

GoIfST ss,bb+,LLLL [D>=12]

GoIfT LLLL

GoIfUF n,m,line

GoIfUT n,m,line

GoIfXEQ nn,LLLL

GoIfXGE nn,LLLL

GoIfXGT nn,LLLL

GoIfXLE nn,LLLL

GoIfXLT nn,LLLL

GoIfXNE nn,LLLL

GoIfZ LLLL

GoSub LLLL

GoSubIfF LLLL

GoSubIfMNZ mm+,LLLL

GoSubIfMZ mm+,LLLL

GoSubIfNZ LLLL

GoSubIfSF ss,bb+,LLLL [D>=12]

GoSubIfST ss,bb+,LLLL [D>=12]

GoSubIfT LLLL

GoSubIfXEQ nn,LLLL

GoSubIfXGE nn,LLLL

GoSubIfXGT nn,LLLL

GoSubIfXLE nn,LLLL

GoSubIfXLT nn,LLLL

GoSubIfXNE nn,LLLL

GoSubIfZ LLLL

GoTo LLLL

OBCA_GoIfNoRdg line

OBCA_GoIfRdg line

OBCA_GoSubIfRdg line

OBLCD_GoIfBusy line

OBLCD_GoIfIdle line

RetIfF

RetIfMNZ mm+

RetIfMZ mm+

RetIfNZ

RetIfT

RetIfZ

Return

GoIfRTCEvent e+,LLLL [D>=16]

Suspend mm*+ [D>=12/15]


Branch

The Branch instruction allows an "n-way" branch on the contents of the X register. A Branch instruction must always be followed by a list of one or more program labels used as arguments in Target instructions. Example

          Branch
          Target      Agin          ;target if X=0
          Target      More          ;target if X=1
          Target      Orless        ;target if X=2

In the example, the program will transfer to label Agin if X=0, to More if X=1 and to Orless if X=2.

For any other value of X the results are unpredictable in SPLat hardware.

The Branch instruction may have up to 127 Targets following it. The SPLat/PC development environment will report an error if a Branch is executed with an X value too large for the number of Target labels provided. (The condition can only be detected by running or stepping the program within SPLat/PC, and not during translation).

Branch is designed to allow efficient coding of Finite State Machines where the value of X is the state number and easily controlled by the programmer.

See also Target BranchM BranchR BranchJ

When using Branch and Target take extra care. If the offset in X points to a line past the end of the Target list, there is no guaranty of what will happen during standalone operation in a SPLat board. This could lead to mysterious problems at runtime. The simulated stepping and running within SPLat/PC will pick up such problems providing every state and branch of the program is exercised.


BranchJ [D>=16]

The BranchJ instruction allows an "n-way" branch on the contents of the J register. A BranchJ instruction must always be followed by a list of one or more program labels used as arguments in Target instructions. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

Example:

          BranchJ
          Target      Agin          ;target if J=0
          Target      More          ;target if J=1
          Target      Orless        ;target if J=2

In the example, the program will transfer to label Agin if J=0, to More if J=1 and to Orless if J=2.

For any other value of J the results are unpredictable in SPLat hardware.

The BranchJ instruction may have up to 127 Targets following it. The SPLat/PC development environment will report an error if a BranchJ is executed with a J value too large for the number of Target labels provided. (The condition can only be detected by running or stepping the program within SPLat/PC, and not during translation).

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


BranchM mm+

Does an n-way branch on the contents of RAM location mm. This is equivalent to the instruction pair:

          Recall    mm
          Branch

except it does not affect the register stack.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


BranchR

The BranchR instruction allows an "n-way" branch on the contents of the R register. A BranchR instruction must always be followed by a list of one or more program labels used as arguments in Target instructions.

The BranchR instruction may have up to 127 Targets following it. SPLat/PC will report an error if a BranchR is executed with an R value too large for the number of Target labels provided. (The condition can only be detected by running or stepping the program within SPLat/PC, and not during translation).

BranchR allows for easy testing of the outcomes of several floating point instructions and the CompareR instruction.

Dialect restriction: This instruction is implemented only in dialects 6 or later.

See also Branch BranchM Target CompareR

When using BranchR and Target take extra care. If the offset in R points to a line past the end of the Target list, there is no guaranty of what will happen during standalone operation in a SPLat board. This could lead to mysterious problems at runtime.The simulated stepping and running within SPLat/PC will pick up such problems


GoIfInK ii+,LLLL

Goes to line LLLL if there is a latched OFF to ON transition stored at input ii.

Executing the GoIfInK instruction clears any latched transition, so a new OFF to ON transition would be required for a second GoIfInK to return a True value.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.

See also InputF, KBeepOn, KBeepOff WaitOnK GoIfInOn GoIfInOff


GoIfINZ LLLL

Evaluates the number in the Index Register I. If I<>0, transfers program control (jumps) to the program line LLLL, otherwise executes the next instruction in line.

See also GoIfIZ


GoIfIZ LLLL

Evaluates the number in the Index Register I. If I=0, transfers program control (jumps) to the program line LLLL, otherwise executes the next instruction in line.

See also GoIfINZ


GoIfMEQ mm+,nn,LLLL

Go to program line LLLL if memory (RAM) location mm contains the number nn.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfMGE mm+,nn,LLLL

Go to program line LLLL if memory (RAM) location mm contains a number greater than or equal to nn. The comparison is made as unsigned integer numbers.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfMGT mm+,nn,LLLL

Go to program line LLLL if memory (RAM) location mm contains a number >nn. The comparison is made as unsigned integer numbers.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfMLE mm+,nn,LLLL

Go to program line LLLL if memory (RAM) location mm contains a number less than or equal to nn. The comparison is made as unsigned integer numbers.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfMLT mm+,nn,LLLL

Go to program line LLLL if memory (RAM) location mm contains a number <nn. The comparison is made as unsigned integer numbers.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfMNE mm+,nn,LLLL

Go to program line LLLL if memory (RAM) location mm contains a number not equal to nn.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfMNZ mm+,LLLL

Go to program line LLLL if memory (RAM) location mm contains a number other than zero.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfMZ mm+,LLLL

Go to program line LLLL if memory (RAM) location mm contains the number zero.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoIfNZ LLLL

This is a conditional GoTo instruction. It tests the contents of X, Popping the register stack in the process, then transfers program control (jumps) to the program line LLLL if X was nonzero or True.

This instruction has the alias (alternative name) GoIfT

See also GoTo, GoIfZ, GoIfXEQ GoIfXNE, GoIfXNE, GoIfXGE, GoIfXLT, GoIfXLE, GoIfF, GoIfT


GoIfRTCEvent e+,LLLL [D>=16]

Test event e and go to program line LLLL if the event is tripped.

An event can only be tested for tripped once per trip. See RTC documentation and tutorial

The event number e will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 16.


GoIfSF ss,bb+,LLLL [D>=12]

Go to line LLLL if semaphore ss is false. The semaphore base address bb is optional; if omitted it is taken to be 0. To omit the base address you can write either ss,,LLLL or ss,LLLL. SPLat/PC can handle either format.

For more details see the semaphore tutorial.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: Available only in boards with dialect 12 or greater.


GoIfSim LLLL

If executing in the SPLat/PC simulator, go to line LLLL. If in a real board, do nothing.

This instruction is purely to assist in debugging using the simulator. It produces no code in the translated program.

Dialect restrictions: None. Exists in SPLat/PC Builds 123 or later.


GoIfST ss,bb+,LLLL [D>=12]

Go to line LLLL if semaphore ss is true. The semaphore base address bb is optional; if omitted it is taken to be 0. To omit the base address you can write either ss,,LLLL or ss,LLLL … SPLat/PC can handle either format.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

For more details see the semaphore tutorial.

Dialect restriction: Available only in boards with dialect 12 or greater.


GoIfUF n,m,line

Go to the named program line if the contents of U(n), when ANDed with argument m, is False (zero).

This instruction takes the contents of U(n) and performs a bit by bit AND with m and does a goto if the result is zero. This gives a mechanism whereby one single byte returned from an SPx peripheral could contain up to 8 individual status bits which can easily be tested individually or in combinations.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


GoIfUT n,m,line

Go to the named program line if the contents of U(n), when ANDed with argument m, is True (non-zero).

This instruction takes the contents of U(n) and performs a bit by bit AND with m and does a GoTo if the result is non-zero. This gives a mechanism whereby one single byte returned from an SPx peripheral could contain up to 8 individual status bits which can easily be tested individually or in combinations.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


GoIfXEQ nn,LLLL

This is a conditional GoTo instruction. It compares the contents of X with the first argument nn and does a GoTo program line LLLL if X=nn. X and nn are in the number range 0-255.

Also pops the stack, so the original content of X is lost.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7

See also GoIfXNE, GoIfXGT, GoIfXGE, GoIfXLT, GoIfXLE, GoIfZ, GoIfNZ


GoIfXGE nn,LLLL

This is a conditional GoTo instruction. It compares the contents of X with the first argument nn and does a GoTo program line LLLL if X is Greater than or Equal to nn. X and nn are in the number range 0-255.

Also pops the stack, so the original contents of X is lost.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7

See also GoIfXEQ, GoIfXNE, GoIfXGT, GoIfXLT, GoIfXLE, GoIfZ, GoIfNZ


GoIfXGT nn,LLLL

This is a conditional GoTo instruction. It compares the contents of X with the first argument nn and does a GoTo program line LLLL if X is Greater Than nn. X and nn are in the number range 0-255.

Also pops the stack, so the original contents of X is lost.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7

See also GoIfXEQ, GoIfXNE, GoIfXGE, GoIfXLT, GoIfXLE, GoIfZ, GoIfNZ


GoIfXLE nn,LLLL

This is a conditional GoTo instruction. It compares the contents of X with the first argument nn and does a GoTo program line LLLL if X is Less than or Equal to nn. X and nn are in the number range 0-255.

Also pops the stack, so the original contents of X is lost.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7

See also GoIfXEQ, GoIfXNE, GoIfXGT, GoIfXGE, GoIfXLT, GoIfXLE, GoIfZ, GoIfNZ


GoIfXLT nn,LLLL

This is a conditional GoTo instruction. It compares the contents of X with the first argument nn and does a GoTo program line LLLL if X is Less Than nn. X and nn are in the number range 0-255.

Also pops the stack, so the original contents of X is lost.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7

See also GoIfXEQ, GoIfXNE, GoIfXGT, GoIfXGE, GoIfXLE, GoIfZ, GoIfNZ


GoIfXNE nn,LLLL

This is a conditional GoTo instruction. It compares the contents of X with the first argument nn and does a GoTo program line LLLL if X is Not Equal to nn. X and nn are in the number range 0-255.

Also pops the stack, so the original contents of X is lost.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=7

See also GoIfXEQ, GoIfXGT, GoIfXGE, GoIfXLE, GoIfZ, GoIfNZ GoIfXLT


GoIfZ LLLL

This is a conditional GoTo instruction. It tests the contents of X, Popping the register stack in the process, then transfers program control (jumps) to the program line LLLL if X was zero (or False).

This instruction has the alias (alternative name) GoIfF

See also GoTo, GoIfNZ, GoIfXEQ, GoIfXNE, GoIfXGT, GoIfXGE, GoIfXLT, GoIfXLE, GoIfF, GoIfT


GoSub LLLL

Remembers where current program line is then transfers control to the subroutine at program line LLLL. A subsequent Return will return control back to the program line following the GoSub. If your program attempts more than 4 nested GoSubs, the SPLat/PC simulator reports an error and the SPLat hardware resets.

See also Return, RetIfZ, RetIfNZ, RetIfT, RetIfF, Gosub, GoSubIfZ, GoSubIfNZ, GoSubIfT, GoSubIfF

Do not confuse with GoTo, which does not remember a line to return to.


GoSubIfF LLLL

This is an alias for (does exactly the same as) GoSubIfZ


GoSubIfMNZ mm+,LLLL

Go to a subroutine at program line LLLL if memory (RAM) location mm contains any non-zero number.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoSubIfMZ mm+,LLLL

Go to a subroutine at program line LLLL if memory (RAM) location mm contains the number 0.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


GoSubIfNZ LLLL

Tests the contents of X, Popping the register stack in the process, then executes a GoSub to the program line LLLL if X was nonzero.

This instruction has the alias (alternative name) GoSubIfT

Dialect restriction: This instruction is not implemented on boards prior to dialect D=3.

See also Return, RetIfZ, RetIfNZ, RetIfT, RetIfF, Gosub, GoSubIfZ, GoSubIfT, GoSubIfF

Do not confuse with GoIfNZ, which does not remember a line to return to.


GoSubIfSF ss,bb+,LLLL [D>=12]

Go to the subroutine at line LLLL if semaphore ss is false. The semaphore base address bb is optional; if omitted it is taken to be 0. To omit the base address you can write either ss,,LLLL or ss,LLLL … SPLat/PC can handle either format.

For more details see the semaphore tutorial.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: Available only in boards with dialect 12 or greater.


GoSubIfST ss,bb+,LLLL [D>=12]

Go to the subroutine at line LLLL if semaphore ss is true. The semaphore base address bb is optional; if omitted it is taken to be 0. To omit the base address you can write either ss,,LLLL or ss,LLLL … SPLat/PC can handle either format.

For more details see the semaphore tutorial.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: Available only in boards with dialect 12 or greater.


GoSubIfT LLLL

This is an alias for (does exactly the same as) GoSubIfNZ


GoSubIfXEQ nn,LLLL

Go to a subroutine at program line LLLL if X contains the number nn.

Dialect exclusions: Not available in dialects before 10.

Note: Does not pop X

See also GoSubIfXGE, GoSubIfXGT, GoSubIfXLE, GoSubIfXLT, GoSubIfXNE


GoSubIfXGE nn,LLLL

Go to a subroutine at program line LLLL if X contains a number greater than or equal to nn.

Dialect exclusions: Not available in dialects before 10.

Note: Does not pop X

See also GoSubIfXEQ, GoSubIfXGT, GoSubIfXLE, GoSubIfXLT, GoSubIfXNE


GoSubIfXGT nn,LLLL

Go to a subroutine at program line LLLL if X contains a number greater than nn.

Dialect exclusions: Not available in dialects before 10.

Note: Does not pop X

See also GoSubIfXEQ, GoSubIfXGE, GoSubIfXLE, GoSubIfXLT, GoSubIfXNE


GoSubIfXLE nn,LLLL

Go to a subroutine at program line LLLL if X contains a number less than or equal to nn.

Dialect exclusions: Not available in dialects before 10.

Note: Does not pop X

See also GoSubIfXEQ, GoSubIfXGE, GoSubIfXGT, GoSubIfXLT, GoSubIfXNE


GoSubIfXLT nn,LLLL

Go to a subroutine at program line LLLL if X contains a number less than nn.

Dialect exclusions: Not available in dialects before 10.

Note: Does not pop X

See also GoSubIfXEQ, GoSubIfXGE, GoSubIfXGT, GoSubIfXLE, GoSubIfXNE


GoSubIfXNE nn,LLLL

Go to a subroutine at program line LLLL if X contains a number not equal to nn.

Dialect exclusions: Not available in dialects before 10.

Note: Does not pop X

See also GoSubIfXEQ, GoSubIfXGE, GoSubIfXGT, GoSubIfXLE, GoSubIfXLT


GoSubIfZ LLLL

Tests the contents of X, Popping the register stack in the process, then executes a GoSub to the program line LLLL if X was zero.

This instruction has the alias (alternative name) GoSubIfF

Dialect restriction: This instruction is not implemented on boards prior to dialect D=3.

See also Return, RetIfZ, RetIfNZ, RetIfT, RetIfF, Gosub, GoSubIfNZ, GoSubIfT, GoSubIfF

Do not confuse with GoIfZ, which does not remember a line to return to.


RetIfF

This is an alias for (does exactly the same as) RetIfZ


RetIfMNZ mm+

Return from subroutine if memory (RAM) location mm contains a non-zero number.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


RetIfMZ mm+

Return from subroutine if memory (RAM) location mm contains the number zero.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


RetIfNZ

Tests contents of X and Pop's stack. Then, if X was nonzero (or True), returns program flow to the line following the most recently executed GoSub instruction.

If a return is attempted when there was no matching GoSub, SPLat/PC reports an error and the SPLat hardware resets.

This instruction has the alias (alternative name) RetIfT

See also Return, RetIfZ RetIfT, RetIfF, Gosub, GoSubIfZ, GoSubIfNZ, GoSubIfT, GoSubIfF


RetIfT

This is an alias for (does exactly the same as) RetIfNZ


RetIfZ

Tests contents of X and Pop's stack. Then, if X was zero (or False), returns program flow to the line following the most recently executed GoSub instruction.

If a return is attempted when there was no matching GoSub, the SPLat/PC simulator reports an error and the SPLat hardware resets.

This instruction has the alias (alternative name) RetIfF

See also Return, RetIfNZ, RetIfT, RetIfF, Gosub, GoSubIfZ, GoSubIfNZ, GoSubIfT, GoSubIfF


Return

Returns program flow to the line following the most recently executed GoSub instruction. If there was no matching GoSub, the SPLat/PC simulator reports an error and the SPLat hardware resets.

See also RetIfZ, RetIfNZ, RetIfT, RetIfF, Gosub, GoSubIfZ, GoSubIfNZ, GoSubIfT, GoSubIfF


Suspend mm*+ [D>=12/15]

Save the memory address of the next line of code in RAM at mm and mm+1, and then perform a Return operation.

See also Resume

This is a special instruction used specifically for implementing multitasking programs. Multitasking tutorial

Dialect restriction: Available only in boards with dialect 12 or greater.

Indexing: From dialect 15 this instruction can be indexed by adding 'i' in front of it. Because suspend/resume addresses occupy 2 bytes of RAM, the index register is multiplied by 2 before being applied to mm.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Note: This method of multitasking, introduced in dialect 12, is rendered largely obsolete by the MultiTrack system introduced in dialect 16.


# Hash commands and functions

Hash commands and hash functions were introduced in January 2012. They are supported in SPLat/PC Build 160 or later.

A hash command is written as a line that starts with a hash character, #, also called sharp or pound sign.

Example:

#	HMI	SetCursor(3, 5)

In many hash functions the arguments can also be given as Name:Value Pairs (NVP) :-

#	HMI	SetCursor(r:3, c:5)

In the above line of code the # in column 1 signals SPLat/PC to process the line as a hash command rather than a regular SPLat language instruction. In this particular example the hash command is HMI, while SetCursor(3, 5) is what we call a hash function. (The distinction between "command" and "function" is important).

One hash command may have zero, one or more hash functions on the same line. This may and will vary between hash commands. Also, each hash command will have its own unique set of allowable hash functions.

Note that the # must be the first non-space (or tab) character on the line. Therefore you can't label a hash command line. You can however have a label on the line above and get the same effect.

This section is organized by hash command. Under each hash command you will find all the matching hash functions. The headings all start with a # character so they will all appear together in the index.

In This Section

Name:Value Pairs in hash functions

Formatting functions within hash functions

ARGB-Hex colour specifiers in hash functions

# 4D_ViSi hash command - MultiTrack+ only!

# HMI hash command

# HMI_Log hash command

# HMI_Trace hash command

# Include hash command

# Open_Serial hash command

# Thermistor hash command


Name:Value Pairs in hash functions

Hash functions generally "take" arguments. For example in

# HMI SetCursor(4, 5)

4 and 5 are the arguments to the SetCursor hash function (they specify the row and column respectively). If the arguments are specified that way, they must be in precisely the right order, separated by commas. If any optional arguments are omitted they must still appear as commas.

Name:Value Pairs (NVP) give you a simpler, less tedious way of specifying arguments, especially in hash functions that take a lot of arguments. NVP arguments also improve the readability of your program.

Example: The line above, re-written with NVP arguments, would be

# HMI SetCursor(r:4, c:5)

It would be equally permissible to reverse the arguments (something you absolutely cannot do with the old list format!):

# HMI SetCursor(c:5, r:4)

The abbreviated names for the arguments, c for column and r for row, are defined in the hash function definition. The names are always very short, to save typing, and may be UPPER or lower case.

In the pages for each hash function, the Names are defined in the page title. If you use NVP, you can list the arguments in any order. If you don't use NVP, you must list them in the order given. Hence,

# HMI SetCursor(r:4, c:5)

and

# HMI SetCursor(c:5, r:4)

are equivalent, but

# HMI SetCursor(4, 5)

and

# HMI SetCursor(5, 4)

are not.

Alternative names

Some arguments can have different names. This most often happens with screen coordinates, where you may chose to call them r and c, or you may prefer y and x. Alternative names are shown with a vertical bar (pipe symbol between the alternatives). See for example the heading for SetCursor. "# SetCursor(r|y:Row, c|x:Col) hash function"


Formatting functions within hash functions

Formatting functions are a way of specifying data memory (RAM) variables or register contents as arguments in hash functions. Some examples:

# HMI Print(b(*Foo))   ;Fetch RAM byte (8-bit) variable Foo and print (display on the HMI screen)
# HMI Print(f(fBar))   ;Fetch RAM floating point variable fBar and print (interpreting it as a float)
# HMI Print(b(=x))     ;Print the byte (8-bit) value in X

The first and last of the above use the b() formatting function. The second one uses the f() formatting function.

At time of writing formatting functions are supported only by HMI related hash functions. They give you a powerful and flexible way of programmatically controlling the display. Each hash function's page states explicitly if it supports formatting functions.

Restrictions:

  1. Name:Value Pairs cannot be used inside formatting functions.
  2. #EQU values cannot be used inside formatting functions.

Previous Topic

Next Topic

In This Section

Byte formatting function b(Source {, Format})

Floating point formatting function f(Source {, FieldWidth, Decimals})

Semaphore formatting function s(Source, TrueOutput, FalseOutput)

Literal formatting function L(item {,item ...})


Byte formatting function b(Source {, Format})

Specifies byte sized (8-bit, values 0 to 255) data as an argument to a supporting hash function. The first argument specifies where to get the data (the source). The second, optional, argument specifies the actual display format. If the format is not specified, the format defaults to decimal, variable width.

While the obvious use of this is in the Print() hash function, it can also be used in such functions as SetCursor or HBar.

Source specifiers

The following are the allowable source specifiers

Form | Meaning | Example
=x | Contents of the X register | #HMI Print(b(=x))
=y | Contents of the Y register | #HMI Print(b(=y))
=h | Contents of the H register | #HMI Print(b(=h))
=m | Contents of the M register | #HMI Print(b(=m))
=s | Contents of the S register | #HMI Print(b(=s))
=i | Contents of the I register | #HMI Print(b(=i))
Constant | Contents of an EQUated constant | Foo EQU 56; ... #HMI Print(b(Foo))
A number 0 - 255 | An immediate numeric value | #HMI Print(b(165))
*Label | Contents of a RAM byte (variable) | Temperature defBYTE; ... #HMI Print(b(*Temperature))

index/jndex modifiers

RAM variable references can also have index/jndex modifiers

Form | What is sourced | Example code
*+bb | Whatever is in RAM (byte) variable bb, with IasJ: applied | #HMI Print(b(*+Counter)) ;Force indexing
*-bb | Whatever is in RAM (byte) variable bb, with NoJ: applied | #HMI Print(b(*-Counter)) ;Inhibit jndexing

NOTE: The modifier (+ or -) must be after the asterisk. jndexing applies only to code running inside a MultiTrack task.

Format specifiers

The format argument specifies how the number is to be displayed. Valid format specifiers are:

Form | Resulting format | Example | Result (the single quotes are NOT printed)
Blank (default) | Decimal, variable width | #HMI Print(b(56)) | '56'
f | Decimal in fixed field width (3 characters) | #HMI Print(b(56, f)) | ' 56'
v | Decimal in variable field width (as many characters positions as are needed. | #HMI Print(b(56, v)) | '56'
2 | Decimal, in 2 character field, padded with 0. Intended for displaying RTC time. | #HMI Print(b(=h, 2)) | '06'
h | Hexadecimal in a field of two character positions | #HMI Print(b(165, h)); #HMI Print(b(10, h)) | 'A5'; '0A'


Floating point formatting function f(Source {, FieldWidth, Decimals})

Specifies floating point data as an argument to a supporting hash function. The first argument specifies the data source. An optional pair of arguments specifies the format in terms of the number of characters total (FieldWidth) and the number of digits after the decimal point (Decimals). Unless both format arguments are present, the format defaults to FieldWidth=9 and Decimals=9. If the result requires fewer than FieldWidth character it will be padded to the left with spaces.

Source specifiers

The following are the allowable source specifiers

Form | Meaning | Example
=w | Contents of the W register | #HMI Print(b(=x))
=q | Contents of the Q register | #HMI Print(b(=y))
Constant | Contents of an EQUated constant. This can be plain EQU, fEQU or #EQU | Foo fEQU 56.78; ... #HMI Print(f(Foo))
Any valid floating point number | An immediate numeric value | #HMI Print(f(3.1415926))
*Label | Contents of a float in RAM | fTemperature defFLOAT; ... #HMI Print(f(*Temperature))

index/jndex modifiers

RAM variable references can also have index/jndex modifiers

Form | What is sourced | Example code
*+ff | Whatever is in RAM (float) variable ff, with IasJ: applied | #HMI Print(f(*+fCounter)) ;Force indexing
*-ff | Whatever is in RAM (float) variable ff, with NoJ: applied | #HMI Print(f(*-fCounter)) ;Inhibit jndexing

NOTE: The modifier (+ or -) must be after the asterisk. jndexing applies only to code running inside a MultiTrack task.

Format specifiers

The format arguments FieldWidth and Decimals specify how the number is to be displayed. The results are consistent with the older character LCD OBLCD_fDispW instruction.

Examples (spaces shown as ^):

Example code | Result (the single quotes are NOT printed) | Notes
#HMI Print(f(1.23456)) | ' 1.23456' | Defaults to 9, 9
#HMI Print(f(1.23456, 9)) | ' 1.23456' | Defaults to 9, 9 because FieldWidth and Decimals must be both present to affect the outcome (all or nothing)
#HMI Print(f(1.23456, 5, 3)) | '1.235' | Note rounding
#HMI Print(f(-1.23456, 5, 3)) | '-1.23' | Limited by f character positions
#HMI Print(f(-1.23456, 5, 9)) | ' -1.235' | Limited by 3 decimal places; padded left with spaces
#HMI Print(f(12.3456, 5, 3)) | '12.35' |
#HMI Print(f(123.456, 5, 3)) | '123.4' |
#HMI Print(f(1234.56, 5, 3)) | ' 1235' | Decimal point suppressed
#HMI Print(f(12345.6, 5, 3)) | '12346' |
#HMI Print(f(123456, 5, 3)) | '+++++' | Overflow: result can't fit in field
#HMI Print(f(-12345.6, 5, 3)) | '-----' | Underflow (negative overflow)
#HMI Print(f(0.123456, 5, 3)) | '0.123' | Leading "0." is displayed
#HMI Print(f(-0.123456, 5, 3)) | '-0.12' |
#HMI Print(f(-0.00012345, 5, 3)) | '-0.000' | Take care with small numbers!


Semaphore formatting function s(Source, TrueOutput, FalseOutput)

The semaphore formatting function provides a convenient way of converting a semaphore value to something readable. It "enables" one of two alternative "messages", depending on whether a semaphore value is true or false. As we shall see, it extends that idea quite a bit further than just semaphores and just text messages. For example:

#HMI Print(s(*sHeater, "Heater is on ", "Heater is off")

If the semaphore sHeater is True, this will display Heater is on . Otherwise it will display Heater is off.

Source specifiers

The following are the allowable source specifiers

Form | Meaning | Example
*sName | The True/False value of a RAM semaphore. It must have been defined in a defSEM. | #HMI Print(s(*sMySem, "True", "False"))
=x | Contents of the X register. Any non-zero value is treated as True. | #HMI Print(s(=x, "NonZero", "Zero"))
=y | Contents of the Y register. Any non-zero value is treated as True. | #HMI Print(s(=y, "NonZero", "Zero"))
=h | Contents of the H register. Any non-zero value is treated as True. | #HMI Print(s(=h, "NonZero", "Zero"))
=m | Contents of the M register. Any non-zero value is treated as True. | #HMI Print(s(=m, "NonZero", "Zero"))
=s | Contents of the S register. Any non-zero value is treated as True. | #HMI Print(s(=s, "NonZero", "Zero"))
Constant | Contents of an EQUated constant. Any non-zero value is treated as True. | Foo EQU 56; ... #HMI Print(s(Foo, "NonZero", "Zero"))
A number 0 - 255 | An immediate numeric value. Any non-zero value is treated as True. | #HMI Print(s(165, "NonZero", "Zero"))

index/jndex modifiers

RAM semaphore references can also have index/jndex modifiers

Form | What is sourced | Example code
*+ss | Whatever is in RAM semaphore ss, with IasJ: applied | #HMI Print(s(*+sMySem, "True", "False"))
*-ss | Whatever is in RAM semaphore ss, with NoJ: applied | #HMI Print(s(*-sMySem, "True", "False"))

NOTE: The modifier (+ or -) must be after the asterisk. jndexing applies only to code running inside a MultiTrack task.

TrueOutput, FalseOutput arguments

The arguments defining what is to be displayed for the true and false alternatives can be any of:

  • A literal text string in quotes;
  • Any other formatting function.

The second of the above masks a great deal of power in this formatting function.

Examples:

  • "Plain vanilla" selection of two text alternatives:
#HMI Print(s(sAlive, "Life is good", "Farewell, oh cruel world")

  • Switch between number and text. In this example, if the sSensorFail semaphore is true, a fault message is displayed. Otherwise, the value of a floating point variable is displayed.
#HMI Print(s(*sSensorFail, "Bad sensor!", f(fTemperature, 5, 1))

  • Nested semaphore formatting functions. This line effectively checks two fault conditions and displays an alarm message if either is tripped, otherwise displays "All good".
# HMI Print(s(*sSensorFail, "Bad sensor", s(*sOverflow, "Overflow", "All good")))


Literal formatting function L(item {,item ...})

The Literal formatting function is a catch-all that lets you send any arbitrary character to the display. Its arguments consist of one or more items, separated by commas.

Items can be:

Form | Description | Example
"string" | Any text string in quotes | #HMI Print(L("Hello World") ;Legal, but probably pointless
Number | An 8-bit number (0 to 255) in SPLat format. This defines the ASCII character that will be sent to the display. | #HMI Print(L('0D, '0A)) ;Send CR/LF to the display

You cannot have other formatting functions inside L()

Note that while the syntax is case-insensitive, it makes sense to use upper case L to avoid confusion with a numeral 1.


ARGB-Hex colour specifiers in hash functions

ARGB-Hex is a compact way of specifying RGB colours plus optional Alpha (transparency). It lets you specify a colour in a single argument to a hash function, rather the in 3 or 4 arguments in functions like SetBGColor. ARGB-Hex combines the 3 or 4 colour components into a single hexadecimal number.

It is best explained by a number of examples. A leading apostrophe is the SPLat way of denoting a hexadecimal number.

Colour name | Alpha. 0 = transparent | Red component | Green component | Blue component | ARGB-Hex
Red, fully opaque | 255 = 'FF | 255 = 'FF | 0 = '00 | 0 = '00 | 'FFFF0000 or 'FF0000
Green, fully opaque | 255 = 'FF | 0 = '00 | 255 = 'FF | 0 = '00 | 'FF00FF00 or '00FF00
Blue, fully opaque | 255 = 'FF | 0 = '00 | 0 = '00 | 255 = 'FF | 'FF0000FF or '0000FF
Red, 25% opaque | 64 = '40 | 255 = 'FF | 0 = '00 | 0 = '00 | '40FF0000

Note that the alpha component is listed first. This is the opposite of the default order in SetBGColor. Alpha does not have to be included if it is 255, so the default alpha is fully opaque.

SPLat/PC contains a colour picker under the Window menu, which returns the RGB-Hex value on the clipboard, i.e. the hex number without Alpha. You can pre-pend you own Alpha. Here are some representative Alpha values:

Hex | Opacity
FF | 100%
C0 | 75%
7F | 50%
3F | 25%
1F | 12.5%
0F | 6%


# 4D_ViSi hash command - MultiTrack+ only!

The 4D_ViSi hash command gives you a very easy way of programming a suitable SPLat board to interact with touch screen displays from 4D Systems that have been set up using 4D's ViSi-Genie programming environment. The ViSi-Genie programming environment makes it very easy to design screen layouts with buttons, instruments and other elements.

To understand this material you need to be thoroughly familiar with how the 4D ViSi-Genie system works. Please study 4D Systems' documentation.

The programming model for ViSi-Genie is based on having several "forms" (screen layouts) that can each contain one or more "objects". Objects are things like gauges and buttons. You design your form layouts, and specify the properties of the objects, using the ViSi-Genie programming environment.

The SPLat can interact with forms and objects via hash functions.

  • Objects can be sent data from the SPLat, for example the position of a gauge or the on/off value of an LED indicator.
  • Objects can also report events back to the SPLat, for example the press of a graphical button or a change in the setting of a graphical slider bar.
  • The display can be sent commands from the SPLat to display a different form (screen layout) or alter its contrast setting.

Can only be used in a MultiTrack+ task

In This Section

# 4D_bWriteObj(ObjType, ObjIndex, aa) hash function

# 4D_fWriteObj(ObjType, ObjIndex, ff) hash function

# 4D_fWriteObjW(ObjType, ObjIndex) hash function

# 4D_ObjEvent(ObjType, ObjIndex, Handler) hash function

# 4D_NackEvent(Handler) hash function

# 4D_SetFormX() hash function

# 4D_WriteContrastX() hash function

# 4D_WriteObj(ObjType, ObjIndex, Value) hash function

# 4D_WriteObjX(ObjType, ObjIndex) hash function

# Port(p) hash function


# 4D_bWriteObj(ObjType, ObjIndex, aa) hash function

Sends the contents of RAM location aa to the nominated 4D object. The single byte value in aa is converted to the 16-bit data expected by ViSi-Genie, so possible values lie between 0 and 255.

ObjType is the 4D systems Object. These are defined in the ViSi-Genie reference manual. ObjIndex is the object number of that particular type.

Executing this hash function will always result in one YieldTask.


# 4D_fWriteObj(ObjType, ObjIndex, ff) hash function

Sends the floating point value at RAM location ff to the nominated 4D object. The 4 bytes of floating point data at ff are converted to the 16-bit integer data expected by ViSi-Genie, so possible values lie between 0 and 65,535.

ObjType is the 4D systems Object. These are defined in the ViSi-Genie reference manual. ObjIndex is the object number of that particular type.

Executing this hash function will always result in one YieldTask.


# 4D_fWriteObjW(ObjType, ObjIndex) hash function

Sends the floating point value in the floating point register W to the nominated 4D object. The floating point data is converted to the 16-bit integer data expected by ViSi-Genie, so possible values lie between 0 and 65,535.

ObjType is the 4D systems Object. These are defined in the ViSi-Genie reference manual. ObjIndex is the object number of that particular type.

Executing this hash function will always result in one YieldTask.


# 4D_ObjEvent(ObjType, ObjIndex, Handler) hash function

Declares a named subroutine as the handler for an event from the nominated 4D object. Any data returned by the 4D object will be in floating point register W.

ObjType is the 4D systems Object. These are defined in the ViSi-Genie reference manual. ObjIndex is the object number of that particular type.

If the object generates an event (say a user presses a button) the system will execute a GoSub to the subroutine at Handler.

Example:

Say you have the following line somewhere in your program. It declares evMyButton to be the subroutine to be called if WinButton (4D terminology) number 23 is pressed on the 4D screen:

k4D_Winbutton   EQU   6    ;4D's object number for a "WinButton"
# 4D_ViSi  4D_Obj_Event(k4D_Winbutton, 23, evMyButton)

Then elsewhere in your program you might have the following handler:

evMyButton:     On      8  ;Turn on output 8 in response to WinButton 23 on the 4D screen

                Return

This is powerful magic ... it takes only a line or two of code to link the press of a graphical (totally unreal) button on a screen to a real-world action, namely turning on an output, which may be driving a relay that launches a rocket.

Some 4D objects return numerical data. For example, a Slider will return the numerical value of the slider when a user moves it. This data is in W when the event handler subroutine is called.


# 4D_NackEvent(Handler) hash function

This is stuff you can safely ignore if it is hard to understand. It is really only needed if you need to trouble-shoot erratic communications with the 4D display

If something goes wrong in the communications between the SPLat and the 4D display (caused, for example, by a loose connection or some electrical noise), the 4D may send a NACK (Negative ACKnowledge) message to the SPLat. Two things happen if this occurs:

  1. The SPLat (courtesy of the under-the-hood code generated by the hash functions) will attempt to repair the communication link by generating a special sequence of messages to the 4D display.
  2. If you have a 4D_NackEvent(Handler) hash function in your program, a GoSub Handler will be executed. You can then have a subroutine with the label Handler (or whatever you want to call it) that does something useful, like for example count the number of NACKs for debugging purposes.

Example:

Say that somewhere in your program you have the following line:

# 4D_ViSi  4D_NackEvent(evGotNack)	;Declare the event handler for 4D NACK events

You could then have an event handler subroutine that looks like this:

;Event handler for 4D NACK events

evGotNack:     IncM        bNACKcount    ;Count NACKs

               Return

bNACKcount:    defBYTE

The protocol handler in SPLat does not have a retry-on-NACK mechanism. Our experience is that if a NACK occurs there has most likely been a loss of message sync, and the most important thing is to re-synchronise the communications.


# 4D_SetFormX() hash function

Causes the 4D ViSi-Genie display to switch to the form (screen layout design) number in X. Form numbers are assigned in the ViSi-Genie design environment.

Executing this hash function will always result in one YieldTask.


# 4D_WriteContrastX() hash function

Sets the display backlight to the number in X. The maximum value is 15. A value of 0 turns the backlight off.

From our observations all non-zero values produce the same backlight intensity. Turning the backlight off does not disable any user controls such as buttons.

Executing this hash function will always result in one YieldTask.


# 4D_WriteObj(ObjType, ObjIndex, Value) hash function

Sends the number Value to the nominated 4D object. Value is converted to the 16-bit integer data expected by ViSi-Genie, so possible values lie between 0 and 65,535.

ObjType is the 4D systems Object. These are defined in the ViSi-Genie reference manual. ObjIndex is the object number of that particular type.

Executing this hash function will always result in one YieldTask.

Example:

k4D_Led   EQU 14	;4D's LED object type
...
# 4D_ViSi  4D_WriteObj(k4D_Led, 6, 1)	;Turn on LED number 6
# 4D_ViSi  4D_WriteObj(k4D_Led, 3, 0)	;Turn off LED number 3


# 4D_WriteObjX(ObjType, ObjIndex) hash function

Sends the contents of the X register to the nominated 4D object. The single byte value in X is converted to the 16-bit data expected by ViSi-Genie, so possible values lie between 0 and 255.

ObjType is the 4D systems Object. These are defined in the ViSi-Genie reference manual. ObjIndex is the object number of that particular type.

Executing this hash function will always result in one YieldTask.


# Port(p) hash function

Valid for use with hash commands: HMI, Open_Serial, and 4D_ViSi

Sets the serial port address to be used for communications.

The default port is mapped to RAM address 251, and is identical to the programming serial port. Until May 2013 that was the only port possible. In June 2013 we started introducing the ability to support more ports, both real (physical) and virtual.

If you do not want to use any port other than 251, you do not need to use the Port hash function.

If you do want to use a port other than 251 (this will be documented for the particular board you are using), you need to use the Port hash function. Port is generally needed in two places:

  1. In the Open_Serial hash command that sets up the serial port (not required if you are using a virtual port)
  2. In, and only in, the very first #HMI hash command in your program, and as the first hash function within that command. This must be the first HMI function that gets processed by the translator ("at compile time"), taking into account the order of multiple files if you are using Builder.

Reminder: A hash command is the first key word after a #. A hash function appears after the command keyword, and supplies various parameters. A given hash function is legal only with one or more specific hash commands.

The following sample code is from the top of a program (the initialisation sequence) that would be using SimpleHMI on an Android device via a third party JY-MCU Bluetooth module connected to the serial port on an EC1 board.

EC1_Serial    EQU      252                        ;Serial port on EC1 "EasyOne"
# Open_Serial Port(EC1_Serial) User(9600,8,N)     ;Initialise the serial port
# HMI Port(EC1_Serial) ConnectEvent(HMI_Connect)  ;Where to GoSub when the SimpleHMI connects
        GoSub           HomeScreen                ;Show the home screen
        LaunchTask      .....  etc .....
        RunTasksForever

Note the use of a symbolic name (EC1_Serial) for the port. This makes it easier to switch to a different port, for example switching between a Bluetooth connected Android device on port 252 and the SimpleHMI client built into SPLat/PC on port 251. However, in boards that do not support multiple serial ports this may cause problems, so should not be done. You can just use the literal port number, e.g Port(251) - 251 being the only port in such boards - or simply not use Port() at all.


# HMI hash command

The HMI hash command is used to control a SPLat "Simple HMI". This can be either the SimpleHMI function built into SPLat/PC, or the SimpleHMI Android (smartphone) app. In either case the SPLat program is identical, so whatever works inside SPLat/PC will also work when an Android device is used as an HMI.

Example:

# HMI    Cursor(5,0) Text("Hello World")

will place the HMI cursor at row 5, column 0 and display the text "Hello World" (providing the serial port has been suitably initialized and the required connection to an HMI device exists).

In the above code line Cursor and Text are hash functions that specify what to do with the HMI. All the hash functions that are possible with the HMI hash command are listed in this section.

Event handlers

Some of the HMI-related hash functions include a parameter for specifying "Event handlers". Example:

# HMI    ButtonEvent(, 3, 4, 5, 10, "Test Button", MyButtonEvent)

When the SPLat controller executes this hash command, it will display button number 1 on the HMI at row 3, column 4, with height 5 and width 10. The label on the button will be "Test button". When an Enduser clicks the button, the SPLat will automatically execute a GoSub MyButtonEvent. It is then up to you to have a subroutine at a line labelled MyButtonEvent which does whatever you want to do when the user clicks the button, then executes a Return. That subroutine is called an Event handler, because it "handles" the "button_click event".

Event handlers are possible only if your program uses MultiTrack. The feature uses just one MultiTrack task.

SimpleHMI Tutorial

In This Section

# ButtonEvent(ID, Row, Col, Height, Width, "text"{, Mode}, ClickHandler) hash function

# ButtonEvent2(id:ID, r|y:Row, c|x:Col, h:Height, w:Width, t:"Text", m:Mode, z:Z-Index, li:LblImage, ri:RelImage, pi:PresImage, rf:RelFGColor, rb:RelBGColor, pf:PresFGColor, pb:PresBGColor, ev:EventHandler)

# Cls() hash function

# ColourWheelEvent(ev:EventHandler) hash function

# ConnectEvent(ev:EventHandler) hash function

# CursorRel(r|y:dRow, c|x:dColumn) hash function

# Enables(Mask) hash function

# fDispW(f, p) hash function

# FloatVar(fArg, f, p) hash function

# GetRTC() hash function

# HBarOld(Row, Col, Height, FSWidth, fArg) hash function

# HBar(r|y:Row, c|x:Col, h:Height, w:FSWidth, v:Value) hash function

# HexVar(bArg) hash function

# uDispXVW() hash function

# HexDispX() hash function

# HideAllButtons() hash function

# IntInputEvent(t|p:"Prompt"{, L:Min}{, h:Max}, ev:EventHandler) hash function

# OldSpeakNumb(fNumb, f, p)

# PopBGColor() hash function

# PopColors() hash function

# PopFGColor() hash function

# Port(p) hash function

# Rulers() hash function

# PushColors() hash function

# Reset({i:BGImage}{, b:BGColour}) hash function

# ScreenWidthEvent(c:C, ev:EventHandler) hash function

# SetBGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# SetCursor(r|y:Row, c|x:Col) hash function

# SetColors(f:FGColor, b:BGColor) hash function

# SetFGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# SetImagePath(PathName)

# SetRTCEvent({EventHandler}) hash function

# SpeakText (t:Text{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

# SpeakNumb(n:Number{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

# Print(Arg1{, Arg2 ...}) hash function

# uDispXFW() hash function

# uDispXVW() hash function


# ButtonEvent(ID, Row, Col, Height, Width, "text"{, Mode}, ClickHandler) hash function

Valid for use with hash command: HMI Name:Value Pairs are not supported

Implemented on the following platforms: Android, Windows.

Mode is optional

Display a button at the specified screen location, with a text label. Raise the specified Event (execute a GoSub to the given line label) when the button is clicked.

The parameters are:

ID

The button number to use. The HMI client contains a fixed number of buttons that can be re-used on different screens. You may nominate which of the buttons to use. Initial releases of the SimpleHMI for Android and Windows has 100 buttons, numbered 1-100. See the latest HMI documentation for details.

If ID is omitted, the SimpleHMI client will automatically allocate one, starting at number 100 and working downwards. If you plan to change the button's parameters, say to dynamically change its text, you must nominate a specific button. Note that all buttons are de-allocated by HideAllButtons.

Row

The screen row where the top of the button is to be positioned. This refers to character rows in the prevailing font size. Row numbers are 0 based, so the top row is row 0.

Col

The screen column where the left of the button is to be positioned. This refers to character columns in the prevailing font size. Column numbers are 0 based, so the start of each line is 0.

Height

The height of the button in multiples of the character height of the current font.

Width

The width of the button in multiples of the character width of the current font.

"text"

The text to be displayed on the button. This must be enclosed in quotation marks. Non-printing characters are not possible

Mode

This is an optional parameter, introduced with the 3rd beta release of the Android SimpleHMI client. It can be totally omitted if not required, ensuring source code compatibility with earlier versions.

There are currently two modes, Default and Repeat. Default mode produces the simple single click per press behaviour. Repeat mode produces an auto-repeat behaviour such as might be used for incrementing or decrementing an alarm time setting by holding down the button.

For default behaviour Mode is simply left empty or omitted.

For autorepeat behaviour Mode is specified as the letter R (for Repeat). The R may be followed optionally, with no separating space) by a number indicating the desired number of clicks per second. If the number is not include it defaults to 5 clicks per second.

When autorepeat is active there is a short delay when the Enduser first touches the button, then clicks are sent to the host at the specified rate. If the button is held for less than the initial delay, a click is sent when it is released.

Example:

# HMI ButtonEvent(, 10, 10, 2, 5, "I repeat", R10, evTestButtonClick)

The resulting button will autorepeat at 10 click events per second.

The repeat rate must not be too fast for the system to handle. System loading is a function of how much work the host must do for each click event, plus the data transmission time of any response sent back to the SimpleHMI client. If you want a fast rate you should test carefully. In particular look out for evidence of clicks continuing to be processed after the button has been released. The system can buffer up a number of messages in serial buffers.

ClickHandler

You must place a subroutine at a program line with this label, which handles the click of the button

Notes:

  1. A given button can be re-used any number of times on different screens.
  2. The click handler will be run automatically when the button is clicked.
  3. The button can only be clicked if it is visible.
  4. A program can contain a maximum of 127 HMI related events

In addition to being simple numbers, the row/column coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a row specifier | As a column specifier
5 | Row number 5 from the top, counting from 0. This can be a fractional number such as 5.5 | Column number 5 from the left, counting from 0
-5 | Row 5 from the bottom, counting from 0. This can be a fractional number such as -5.25 | Column 5 from the right, counting from 0
0 | The top character row | The left-most character column
-0 | The bottom character row | The right-most character column
0.25 | One quarter of (0.25 times) the screen height from the top. Fractional number between -1 and +1 are interpreted as a fraction of the screen. | One quarter of the screen width from the left.
-0.25 | One quarter of the screen height from the bottom. | One quarter of the screen width from the right.
C | The top-bottom centre of the screen. | The left-right centre of the screen.
C+5 | Five character rows down from the centre | Five character widths right from the centre.
C-5 | Five character rows up from the centre | Five character width left from the centre.
C+.25 | One quarter character row down from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | One quarter character width right from the centre.
C-3.25 | Three and a quarter character rows up from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | Three and a quarter character widths left from the centre.

In addition to being simple numbers, the width and height coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a height specifier | As a width specifier
5 | 5 character rows in height | 5 character rows in width
0.25 | One quarter of (0.25 times) the screen height. Always calculated as a full number of rows, always rounded down, but forced to be at least 1. | One quarter of (0.25 times) the screen width. Always calculated as a full number of columns, always rounded down, but forced to be at least 1.


# ButtonEvent2(id:ID, r|y:Row, c|x:Col, h:Height, w:Width, t:"Text", m:Mode, z:Z-Index, li:LblImage, ri:RelImage, pi:PresImage, rf:RelFGColor, rb:RelBGColor, pf:PresFGColor, pb:PresBGColor, ev:EventHandler)

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430


# Cls() hash function

Valid for use with hash command: HMI

Implemented on the following platforms: HMi430, Android, Windows.

Clears the screen of all text and fills it with the prevailing background colour. Locates the text cursor to the top lefthand corner. Has no effect on buttons or any background image.

Interaction with Reset()

The Cls() hash function is normally executed after Reset(). Reset() has the ability to define a background image and the initial background colour (which defaults to black). If Cls() is executed before any change is made to the background colour, then that initial colour will prevail. If, however, you change the background colour between Reset() and Cls(), then that new colour will prevail. This could have the effect of masking a background image.

Fading a background image in Android

In Android the display is formed by placing different elements on "layers", stacked one on top of another. This is called Z-ordering. The background image is placed on the very bottom (back) layer. The background colour is rendered on a layer in front of the image (closer to the viewer). Hence the background colour can mask the image. You can take advantage of this to dim the background image. If the background colour is made black, and its alpha is varied, the visual effect will be to dim the image. You can see the result on YouTube here.

See also Reset, which has a "stronger" effect.


# ColourWheelEvent(ev:EventHandler) hash function

Valid for use with hash command: HMI. Can also be spelled ColorWheelEvent.

Implemented on the following platforms: Android

Interactive Colour selector for RGB LED controls.

Displays a colour wheel image (colour picker) at the top of the screen, scaled to fill the width. Raises events (GoSub EventHandler) as the Enduser moves the touch across the wheel. The non-coloured parts of the colour wheel image are transparent, so the background colour will show through. However, if the touch wanders into those areas the function returns black. The small dots provide pure R, G, B and white colours.

In EventHandler you can retrieve the RGB colour values (range 0-255) by 3 successive COMRx_fGetNum 255 instructions.

The following screen capture show the colour wheel. The buttons at the bottom are separately generated by the target app.

Example: Here is the code snippet from the target app.:

;------ Colour wheel ------------------
evWheel:	                                   ;Event called from top level menu
# HMI Reset() Cls() HideAllButtons()
# HMI ColourWheel(evColourWheel)
# HMI ButtonEvent(, 0.95, 0.6, 0.1, 0.4, "Capture", evSaveMood)
        GoSub           MakeBackButton
        Return

;New colour input from the colour wheel

evColourWheel:

        LaunchTask      tskCW

        COMRx_fGetNum   255

        fix

        Store           bTriRed		;Byte variable, defined elsewhere

        COMRx_fGetNum   255

        fix

        Store           bTriGreen

        COMRx_fGetNum   255

        fix

        Store           bTriBlue

        GoSub           UpdateTriLED   ;Updates the RGB LED

        Return

tskCW:  ;(Diagnostic) Flash a front panel LED on MS120

        On              11

        Pause           10

        Off             11

        KillTask

MakeBackButton:

# HMI ButtonEvent(, 0.95, 0.0, 0.1, 0.4, "Main menu", evTopMenu)

        Return


# ConnectEvent(ev:EventHandler) hash function

Valid for use with hash command: HMI

Implemented on the following platforms: HMi430, Android, Windows.

HMI430

The HMI430 does not have a concepts of connected or not connected. Instead the Connect event is used to signal two other status changes that are unique to the HMi430:

  • Pressing the screen while powering up the device, and
  • Changes in the backlight status

Screen press during power-up

The intention is for this to be used to force the controller into screen calibrate mode. If the screen module itself has had to be replaced, the touch function needs to be re-calibrated.

Change in backlight status

The Connect event gives you an opportunity to perform some external action, say turn on a room light.

HMI430 sample program

When the Connect event is triggered, there will be some additional bytes of data available that you can use to tell what triggered the event. The following code extracts that data and actions accordingly.

COMHMI            EQU   251                           ;"Port" number for the display
evConnected:
   LoadX          5                                   ;does the buffer hold a..
   aCOMRx_FindXInBuf COMHMI                           ; .. end of message (5)?
   GoIfXeq        255,Return                          ;jump if not

   aCOMRx_ReadOne COMHMI                              ;get the message id from the comms buffer

   Push

;Test what kind of event it was ...

   GoIfXeq        0,_UIProcessBacklight               ;jump if message is backlight message

   GoIfXeq        1,_UIPowerOnTouch                   ;jump if user has been touching the screen since power on

Return:

   Return

_UIProcessBacklight:

   aCOMRx_ReadOne COMHMI                              ;get the separator

   GoIfXne        2,Return                            ;abort if not separator

   aCOMRx_ReadOne COMHMI                              ;get the backlight state. TRUE = ON

   aCOMRx_ReadOne COMHMI                              ;get the terminator

   GoIfXne        5,Return                            ;abort if not terminator

....  insert your code to action backlight status change. X is TRUE if the light is on

   Return

_UIPowerOnTouch:

   aCOMRx_ReadOne COMHMI                              ;get the endhdr

   GoIfXne        5,Return                            ;abort if not endhdr

   SPxCmd0        0,10                                ;go perform cal, won't return until complete

   GoSub          UIsubScreenShowIdle                 ;redraw the idle screen

   Return

Android and Windows

This function specifies an Event handler that is to be called when the HMI reports it has become connected. The typical scenario is an Android device that is not normally connected to a SPLat controller. When the Android HMI app is first launched, the user has to enable Bluetooth, make sure it is paired with the Bluetooth on the SPLat, and select the SPLat Bluetooth to connect to. All this while the HMI in the Android is displaying a default screen. Once the Bluetooth connection is successfully established, the Android HMI app sends a special code to the SPLat. The ConnectEvent function detects this and does a GoSub to your Event handler. Your event handler then draws the initial screen applicable to your SPLat application.

The HMI function in SPLat/PC has a Connect button that generates the same code to the SPLat.


# CursorRel(r|y:dRow, c|x:dColumn) hash function

Valid for use with hash command: HMI. Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android, Windows (except Alpha)

Moves the cursor for subsequent Text relative to its current position. dRow and dCol can be positive or negative numbers, including numbers like 4.5 or -3.25. The number is always interpreted as a multiple of the default character size.


# Enables(Mask) hash function

Valid for use with hash command: HMI

Implemented on the following platforms: Windows standalone

This function determines which controls will be available on the SimpleHMI screen. Mask consists of 8 semaphores packed into a single byte. Each semaphore controls the visibility of one item. This allows you to control what your Enduser will see in the standalone Windows version of SimpleHMI.

The following defines the bit (semaphore) numbers within the byte, and what they control:

Bit | Controls | Notes
0 | Connect button |
1 | Reset button |
2 | CLS button |
3 | Rulers button |
4 | A/R (aspect ratio) settings |
5 | Width settings |
6 | Show log button | The enduser can still set a log file name on the settings tab, so you can implement logging in your app.
7 | Terminal tab | Also controls relevant settings on the Settings tab. The terminal is by default hidden in the standalone Windows SimpleHMI. It can be made visible by the command-line/shortcut switch /T

Example program:

The following program produces a SimpleHMI screen that can be used to control the visibility of all controllable items. In a practical application you would most likely just send a fixed pattern to the SimpleHMI when you draw the initial screen.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;========= Test enables in standalone sHMI =========================
#       Open_Serial User(38400,8,N)
        GoSub           HMI_Connect
#       HMI ConnectEvent(HMI_Connect)
        RunTasksForever

;ON connecting to SimpleHMI, force the screen to a known width
HMI_Connect:
# HMI Reset() Cls() HideAllButtons() ScreenWidthEvent(40,evScreenWidth)
        Return

;Once the SimpleHMI acknowledges the screen width, draw the buttons.
evScreenWidth:
# HMI Cursor(1, C-10) Print("Test control enables")
# HMI ButtonEvent(, 0.2, 0.1, 0.1, 0.3, "Connect", evConnect)
# HMI ButtonEvent(, 0.2, 0.6, 0.1, 0.3, "Reset", evReset)

# HMI ButtonEvent(, 0.4, 0.1, 0.1, 0.3, "Cls", evCls)

# HMI ButtonEvent(, 0.4, 0.6, 0.1, 0.3, "Ruler", evRuler)

# HMI ButtonEvent(, 0.6, 0.1, 0.1, 0.3, "A/R", evAspect)

# HMI ButtonEvent(, 0.6, 0.6, 0.1, 0.3, "Width", evWidth)

# HMI ButtonEvent(, 0.8, 0.1, 0.1, 0.3, "Show log", evShowLog)

# HMI ButtonEvent(, 0.8, 0.6, 0.1, 0.3, "Terminal", evTerminal)

;Controlling enables. One semaphore byte

bEnables        defBYTE

;Define the enable semaphore bits

sConnect        EQU     0

sReset          EQU     1

sCls            EQU     2

sRuler          EQU     3

sAspect         EQU     4

sWidth          EQU     5

sShowLog        EQU     6

sTerminal       EQU     7

;Event handlers for each of th enable buttons.

evConnect:      LoadI   sConnect

                GoTo    ToggleEnable

evReset:        LoadI   sReset

                GoTo    ToggleEnable

evCls:          LoadI   sCls

                GoTo    ToggleEnable

evRuler:        LoadI   sRuler

                GoTo    ToggleEnable

evAspect:       LoadI   sAspect

                GoTo    ToggleEnable

evWidth:        LoadI   sWidth

                GoTo    ToggleEnable

evShowLog:      LoadI   sShowLog

                GoTo    ToggleEnable

evTerminal:     LoadI   sTerminal

                GoTo    ToggleEnable

ToggleEnable:

                IasJ:RecallS    0,bEnables

                Not

                IasJ:StoreS     0,bEnables

# HMI Enables(bEnables)                         ;Send enables mask to SimpleHMI

                Return

The Enables hash function is intended for use in cases where the standalone SimpleHMI for Windows is to be used by an enduser. It also works in the SPLat/PC version.

In the SPLat/PC version, you can restore the default settings by holding down SHIFT and clicking the blue SimpleHMI title bar.


# fDispW(f, p) hash function

Valid for use with hash commands: HMI, HMI_Trace and HMI_Log

Implemented on the following platforms: HMi430, Android, Windows

This is an old function. Print is newer and shinier. With formatting functions it can do what this one does, and a lot more.

This function will cause the floating point number in W to be output in a field width of f characters total, including p digits after the decimal point. The formatting is identical to OBLCD_fDisp.

Example:

The following program will generate a running time display in the top lefthand corner of the SimpleHMI.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

# Open_Serial User(38400,8,N)
        on      0
        LaunchTask      RunTime        ;Task that generates the running time
        RunTasksForever

fRunningTime:           defFLOAT       ;Used to count up seconds

RunTime:

        Pause 100                      ;1S delay

        fRecallW        fRunningTime   ;Increment the counter ...

        fInc

        fStore          fRunningTime

# HMI Cursor(0,0) SetFGColour(255,0,0) SetBGColor(0,255,255) fDispW(5,0) Print(" ")

        GoTo            RunTime

The hash command Open_Serial is documented elsewhere. Here it simply opens the serial port for use with a user defined protocol at 38400 Baud.


# FloatVar(fArg, f, p) hash function

Valid for use with hash commands: HMI, HMI_Trace and HMI_Log

Implemented on the following platforms: HMi430, Android, Windows

This is an old function. Print is newer and shinier. With formatting functions it can do what this one does, and a lot more.

This function will cause the floating point number stored in RAM at location fArg to be output in a field width of f characters total, including p digits after the decimal point. The formatting is identical to OBLCD_fDisp.

Example:

The following program will generate a running time display in the top lefthand corner of the SimpleHMI.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

# Open_Serial User(38400,8,N)
        on      0
        LaunchTask      RunTime        ;Task that generates the running time
        RunTasksForever

fRunningTime:           defFLOAT       ;Used to count up seconds

RunTime:

        Pause 100                      ;1S delay

        fRecallW        fRunningTime   ;Increment the counter ...

        fInc

        fStore          fRunningTime

# HMI Cursor(0,0) SetFGColour(255,0,0) SetBGColor(0,255,255) FloatVar(fRunningTime,5,0) Print(" ")

        GoTo            RunTime

The hash command Open_Serial is documented elsewhere. Here it simply opens the serial port for use with a user defined protocol at 38400 Baud.


# GetRTC() hash function

Valid for use with hash command: HMI

Implemented on the following platforms: Android, Windows

Request the SimpleHMI client to send its current date and time information. This allows the SPLat controller to tap into the HMI device to set its own RTC.

This hash function only makes sense if you also have a SetRTCEvent, and if the controller you are programming has a Real Time Clock (RTC).

When GetRTC is executed, providing a serial port has been appropriately opened for SimpleHMI use, the board will send a message to the SimpleHMI device asking it to send back the date and time from its internal clock. When the SimpleHMi device responds, the SPLat board will automatically update its own RTC. If the SetRTCEvent specifies a handler, it will be executed immediately after the internal RTC has been updated. There's a fully worked example for the EC1 controller online here.

See also SetRTCEvent, Open_Serial, User


# HBarOld(Row, Col, Height, FSWidth, fArg) hash function

This was originally named HBar. It has been re-named HBarOld to make room for a shiny newer version. Deprecated: DO NOT USE FOR NEW PROGRAMS

Valid for use with hash command: HMI

This function will draw one bar of a horizontal bar graph. This is useful for indicating analog values graphically.

The parameters are:

Row | The screen row where the top of the bar is to be positioned. This refers to character rows in the default font size. Row numbers are 0 based, so the top row is row 0.
Col | The screen position where the left of the bar is to be positioned. This refers to character columns in the default font size. Column numbers are 0 based, so the start of each line is 0. See the table below for more detail.
Height | The height of the bar. See the table below for more detail.
FSWidth | The full scale width of the bar. This is the screen width that will be occupied by the maximum fArg value'
fArg | The name of a floating point variable containing the value to be plotted. Must be between 0.0 and 1.0

The bar is drawn by drawing a rectangle in the current background colour, then on top of that drawing a left-justified rectangle in the current foreground colour whose horizontal length is FSWidth * fArg. fArg should lie in the range 0.0 to 1.0.

In addition to being simple numbers, the row/column coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a row specifier | As a column specifier
5 | Row number 5 from the top, counting from 0. This can be a fractional number such as 5.5 | Column number 5 from the left, counting from 0
-5 | Row 5 from the bottom, counting from 0. This can be a fractional number such as -5.25 | Column 5 from the right, counting from 0
0 | The top character row | The left-most character column
-0 | The bottom character row | The right-most character column
0.25 | One quarter of (0.25 times) the screen height from the top. Fractional number between -1 and +1 are interpreted as a fraction of the screen. | One quarter of the screen width from the left.
-0.25 | One quarter of the screen height from the bottom. | One quarter of the screen width from the right.
C | The top-bottom centre of the screen. | The left-right centre of the screen.
C+5 | Five character rows down from the centre | Five character widths right from the centre.
C-5 | Five character rows up from the centre | Five character width left from the centre.
C+.25 | One quarter character row down from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | One quarter character width right from the centre.
C-3.25 | Three and a quarter character rows up from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | Three and a quarter character widths left from the centre.

In addition to being simple numbers, the width and height coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a height specifier | As a width specifier
5 | 5 character rows in height | 5 character rows in width
0.25 | One quarter of (0.25 times) the screen height. Always calculated as a full number of rows, always rounded down, but forced to be at least 1. | One quarter of (0.25 times) the screen width. Always calculated as a full number of columns, always rounded down, but forced to be at least 1.

Example. The following program will produce the result shown below. The bar is drawn with a red foreground and white background. The buttons can be used to change the bar length:

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;Demonstrate HbarOld hash function
#       Open_Serial User(38400,8,N)
# HMI ConnectEvent(HMI_Connected)
        GoSub           HMI_Connected
        fLoadW          0.4            ;An initial value for the bar
        fStore          fHValue
        LaunchTask      Test
        RunTasksForever

fHValue defFLOAT

;---------------------------------------------------------------

Test:

        Pause           20

# HMI SetBGColour(255,255,255) SetFGColour(255,0,0)

# HMI HBarOld(3,4,2,32,fHValue)

# HMI SetFGColour(0,0,0) Cursor(1,1) floatVar(fHvalue,7,6)

        GoTo            Test

;---------------------------------------------------------------

;Button Event handlers. Let the user change the value plotted

EvMinusP01:

        fLoadQ          -0.01

        GoTo            ChangeVal

EvPlusP01:

        fLoadQ          0.01

        GoTo            ChangeVal

EvPlusP1:

        fLoadQ          0.1

        GoTo            ChangeVal

EvMinusP1:

        fLoadQ          -0.1

ChangeVal:

        fRecallW        fHValue

        fAdd

        fStore          fHValue

        Return

;---------------------------------------------------------------

;Connect Event (and startup)

HMI_Connected:

# HMI Reset() HideAllButtons() SetBGColour(100,100,150) Cls()    ;Blue screen

# HMI ButtonEvent(,10,5,3,10,"-0.1",EvMinusP1)         ;Buttons to alter value

# HMI ButtonEvent(,10,25,3,10,"+0.1",EvPlusP1)

# HMI ButtonEvent(,14,5,3,10,"-0.01",EvMinusP01)

# HMI ButtonEvent(,14,25,3,10,"+0.01",EvPlusP01)

         Return


# HBar(r|y:Row, c|x:Col, h:Height, w:FSWidth, v:Value) hash function

Valid for use with hash command: HMI. Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android, Windows

This function will draw one bar of a horizontal bar graph. This is useful for indicating analog values graphically.

Replaces HBarOld, which is deprecated

The parameters are:

Row | The screen row where the top of the bar is to be positioned. This refers to character rows in the default font size. Row numbers are 0 based, so the top row is row 0.
Col | The screen position where the left of the bar is to be positioned. This refers to character columns in the default font size. Column numbers are 0 based, so the start of each line is 0. See the table below for more detail.
Height | The height of the bar. See the table below for more detail.
FSWidth | The full scale width of the bar. This is the screen width that will be occupied by the maximum fArg value'
Value | A floating point variable containing the value to be plotted. Must be between 0.0 and 1.0. Generally specified with a float formatting function.

The bar is drawn by drawing a rectangle in the current background colour, then on top of that drawing a left-justified rectangle in the current foreground colour whose horizontal length is FSWidth * Value. Value should lie in the range 0.0 to 1.0.

In addition to being simple numbers, the row/column coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a row specifier | As a column specifier
5 | Row number 5 from the top, counting from 0. This can be a fractional number such as 5.5 | Column number 5 from the left, counting from 0
-5 | Row 5 from the bottom, counting from 0. This can be a fractional number such as -5.25 | Column 5 from the right, counting from 0
0 | The top character row | The left-most character column
-0 | The bottom character row | The right-most character column
0.25 | One quarter of (0.25 times) the screen height from the top. Fractional number between -1 and +1 are interpreted as a fraction of the screen. | One quarter of the screen width from the left.
-0.25 | One quarter of the screen height from the bottom. | One quarter of the screen width from the right.
C | The top-bottom centre of the screen. | The left-right centre of the screen.
C+5 | Five character rows down from the centre | Five character widths right from the centre.
C-5 | Five character rows up from the centre | Five character width left from the centre.
C+.25 | One quarter character row down from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | One quarter character width right from the centre.
C-3.25 | Three and a quarter character rows up from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | Three and a quarter character widths left from the centre.

In addition to being simple numbers, the width and height coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a height specifier | As a width specifier
5 | 5 character rows in height | 5 character rows in width
0.25 | One quarter of (0.25 times) the screen height. Always calculated as a full number of rows, always rounded down, but forced to be at least 1. | One quarter of (0.25 times) the screen width. Always calculated as a full number of columns, always rounded down, but forced to be at least 1.

Example. The following program will produce the result shown below on an HMI430. The bar is drawn with a red foreground and white background. The buttons can be used to change the bar length:

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;======= Demonstrate Hbar hash function.  For HMI430 ==================
;Lacks serial port assignment/initialisation required to work on Android or Windows
        GoSub           PaintHomeScreen
        fLoadW          0.4            ;An initial value for the bar
        fStore          fHValue
        GoSub           DrawGraph
        RunTasksForever

fHValue defFLOAT

;---------------------------------------------------------------

PaintHomeScreen:

# HMI Reset() HideAllButtons() SetColours(f:'FFFFFF, b:'0) Cls()    ;Blue screen

# HMI  SetColours(f:'FFFFFF, b:'4040C0)      ;For buttons

# HMI ButtonEvent2(r:6, c:5, h:3, w:10, t:"-0.1", ev:EvMinusP1)         ;Buttons to alter value

# HMI ButtonEvent2(r:10, c:5, h:3, w:10, t:"+0.1", ev:EvPlusP1)         ;Buttons to alter value

# HMI ButtonEvent2(r:6, c:25, h:3, w:10, t:"-0.01", ev:EvMinusP01)         ;Buttons to alter value

# HMI ButtonEvent2(r:10, c:25, h:3, w:10, t:"+0.01", ev:EvPlusP01)         ;Buttons to alter value

         Return

;---------------------------------------------------------------

;Button Event handlers. Let the user change the value plotted

EvMinusP01:

        fLoadQ          -0.01

        GoTo            ChangeVal

EvPlusP01:

        fLoadQ          0.01

        GoTo            ChangeVal

EvPlusP1:

        fLoadQ          0.1

        GoTo            ChangeVal

EvMinusP1:

        fLoadQ          -0.1

ChangeVal:

        fRecallW        fHValue

        fAdd

        fStore          fHValue

;-----------------------------------------------------------------

DrawGraph:

# HMI SetColours(b:'FFFFFF, f:'FF0000)

# HMI HBar(r:3, c:4, h:2, w:40, v:f(*fHValue, 4, 2))

# HMI SetColours(f:'000000, b:'00000000) SetCursor(3, 20) Print(f(*fHvalue, 4, 2))

        Return


# HexVar(bArg) hash function

Valid for use with hash commands: HMI, HMI_Trace, and HMI_Log

Implemented on the following platforms: HMi430, Android, Windows

This is an old function. Print is newer and shinier. With formatting functions it can do what this one does, and a lot more.

This function will cause the 8-bit number stored in RAM at location bArg to be output in hexadecimal format in a field width of 2 characters. The formatting is identical to OBLCD_HexDispX

Example:

The following program will generate a running 8-bit hexadecimal count in the top lefthand corner of the SimpleHMI screen.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

# Open_Serial User(38400,8,N)
        on      0
        LaunchTask      HexCount        ;Task that generates count
        RunTasksForever

bHexNum:           defBYTE

RunTime:

        Pause 100                      ;1S delay

        IncM            bHexNum

# HMI Cursor(0,0) SetFGColour(0,0,255) SetBGColor(255,255,255) Print(" ") HexVar(bHexNum) Text(" ")

        GoTo            RunTime

The hash command Open_Serial is documented elsewhere. Here it simply opens the serial port for use with a user defined protocol at 38400 Baud.


# uDispXVW() hash function

Valid for use with hash commands: HMI, HMI_Trace, and HMI_Log

Implemented on the following platforms: HMi430, Android, Windows

This is an old function. Print is newer and shinier. With formatting functions it can do what this one does, and a lot more.

This function will cause the 8-bit number in X to be output in unsigned decimal format in a variable width field. The formatting is identical to OBLCD_UDecDispXVW. This means the displayed number will occupy only the exact number of character spaces needed, i.e. one character for the number 6, two for numbers 10-99, and three for numbers 100-255.


# HexDispX() hash function

Valid for use with hash commands: HMI, HMI_Trace, and HMI_Log

Implemented on the following platforms: HMi430, Android, Windows

This is an old function. Print is newer and shinier. With formatting functions it can do what this one does, and a lot more.

This function will cause the 8-bit number in X to be output in hexadecimal format in a field width of 2 characters. The formatting is identical to OBLCD_HexDispX

Example:

The following program will generate a running 8-bit hexadecimal count in the top lefthand corner of the SimpleHMI screen.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

# Open_Serial User(38400,8,N)
        on      0
        LaunchTask      HexCount        ;Task that generates count
        RunTasksForever

bHexNum:           defBYTE

RunTime:

        Pause 100                      ;1S delay

        IncM            bHexNum

        Recall          bHexNum

# HMI Cursor(0,0) SetFGColour(0,0,255) SetBGColor(255,255,255) Print(" ", b(X, h), " ")

        GoTo            RunTime

The hash command Open_Serial is documented elsewhere. Here it simply opens the serial port for use with a user defined protocol at 38400 Baud.


# HideAllButtons() hash function

Valid for use with hash command: HMI

Hides (makes invisible) all the buttons on the HMI, resets all their parameters, and de-allocates them. Typically used before generating a new screen.


# IntInputEvent(t|p:"Prompt"{, L:Min}{, h:Max}, ev:EventHandler) hash function

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android, Windows

Gets the user to enter an unsigned integer number.

When executes this will generate a pop-up that prompts the Enduser to supply a number. The parameter "Prompt" is a text string that will be presented to the user. Min and Max are optional upper and lower limits. EventHandler is the line label of a subroutine you must supply that handles the response.

The Min and Max limits, if supplied, are enforced such that the enduser cannot "OK" a value that lies outside the limits. If not supplied Min is 0 and Max is 999,999,999.

When your event handler is called, X will indicate if the enduser entered a number (X = True) or clicked Cancel (X = False). If a number was entered, it will be in W.

Sample code snippet:

        #HMI IntInputEvent(p:"Delay time", l:25, h:250, ev:evSetDelay)

...

evSetDelay:     ;Handle user's change of delay time. X is False if user hit cancel. Answer is in W

        RetIfF          ; r/ user cancelled

        fStoreW         fDelayTime      ;I can just save it, because IntInputEvent already filtered out-of-range values

        Return


# OldSpeakNumb(fNumb, f, p)

Was SpeakNumb, but that name has been re-purposed to a new, more sophisticated hash function which we expect to release in August 2014.

Valid for use with hash command: HMI

Implemented only on the Android version of SimpleHMI. In the Windows versions produces a message box.

Speaks out loud a floating point number.

fNumb is the floating variable name.

f is the field width

p is the number of decimal places.

The number is converted to text, then sent to the Android device for conversion to speech.

Example:

See the example under SpeakText.


# PopBGColor() hash function

Valid for use with hash command: HMI

Retrieves the latest entry off the stack of foreground and background colours and restores the background colours. Foreground colour is unchanged.. The colour stack is described under PushColors.

You may also use the keyword PopBGColour (UK English spelling)


# PopColors() hash function

Valid for use with hash command: HMI

Retrieves the latest entry off the stack of foreground and background colours and restores both colours. The colour stack is described under PushColors.

You may also use the keyword PopColours (UK English spelling)


# PopFGColor() hash function

Valid for use with hash command: HMI

Retrieves the latest entry off the stack of foreground and background colours and restores the foreground colours. Background colour is unchanged. The colour stack is described under PushColors.

You may also use the keyword PopFGColour (UK English spelling)


# Rulers() hash function

Valid for use with hash command: HMI

Implemented on the following platforms: Android, Windows

Draws horizontal and vertical character pitch rulers on the device screen to assist in program development. The SimpleHMI screen in SPLat/PC also has a Ruler button that has the same effect.


# PushColors() hash function

Valid for use with hash command: HMI

In the HMI saves the current foreground and background colours on a last in-first out stack. Useful if you have several tasks displaying stuff on different parts of the screen.

You may also use the keyword PushColours (UK English spelling)


# Reset({i:BGImage}{, b:BGColour}) hash function

Valid for use with hash command: HMI. Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android, Windows (with no arguments).

Restores the HMI to its initial state. The SimpleHMI in SPLat/PC also has a button for doing this, to assist debugging.

BGImage is an optional background image file name. If the image file is found, it will form the background (be positioned behind everything else).

BGColour is an optional background colour specifier in ARGB-Hex format.

The interaction between the image and colour are defined in this table:

Image | Colour | Effect
No | No | Background colour is set to black, any existing image is removed
No | Yes | Background colour is set, any existing image is removed
Yes | No | The image is displayed and the background colour is set to transparent
Yes | Yes | The background colour, with alpha, appears in front of the image.

Example:

This program will display a background image, some text over the image, and a button. It is written for the SPLat EC1 using an HC-06 Bluetooth adaptor at 9600BPS, talking to an Android device.

HMIPort         EQU     252		;Bluetooth on port 252 on EC1
#       Open_Serial Port(HMIPort) User(9600,8,N)
#       HMI Port(HMIPort)  ConnectEvent(evBGImage)
        GoSub           evBGImage    	;Paint the display at startup
        LaunchTask      tskNULL		;A do-nothing task, because I must have something!
        RunTasksForever
;--------------------------------------------------
;Clear the screen, display background and a message. This "event handler" subroutine is run
;at startup and whenever an Enduser clicks the Refresh button on the device screen.
evBGImage:
# HMI   Reset(i:"flower.jpg")	;Reset the screen, display a background image
# HMI   HideAllButtons()         ;Get rid of the default buttons
# HMI   SetBGColour(20, 20, 120)  SetFGColour(255, 0, 255) ;Set a default background, in case the image file is absent
# HMI   Cls()                    ;Remove existing text, set the screen background colour if the image fails
# HMI   Cursor(15, 5) SetBGColour(255, 0, 0, 0)  ;Note the BGColour has a 4th parameter, Alpha (transparency)
# HMI   Text("Text with transparent background")
# HMI   ButtonEvent(, -2, -10, 2, 10, "Refresh", evBGImage)
        Return

tskNULL:   ;There must be at least one running task for SimpleHMI to work.

        YieldTask

        GoTo            tskNULL

  SimpleHMI screen with BGImage

The above program will only work if the named image file is present on the Android device, in the default application image folder - see SetImagePath. You will need to place your own image in the device and ensure the name matches that in the program. If it is not there, the background will be a solid colour. You can experiment with the effects of not finding the image by renaming the file in the Android device (or in the program).

Note: File names in Android are case-sensitive.

To use the program on an MMi203, MS120, CC18 or other 8-bit SPLat board with a single serial port shared between programming and Bluetooth, change the first 2 lines to the following:

HMIPort         EQU     251		;Bluetooth on port 251 on MMi203, MS121, CC18, SL100 etc.

#       Open_Serial Port(HMIPort) User(38400,8,N)   ;Higher baudrate on these boards

See also SetImagePath, which describes file paths and their management.


# ScreenWidthEvent(c:C, ev:EventHandler) hash function

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: Android, Windows

C is an integer number of characters.

Lets you set the screen width to c characters across. SimpleHMI will determine the largest character size that will fit the required number of characters across the screen. This will not be able to use the full width of the screen if C is not divisible into the pixel width of the screen. For example, an Android phone with 320 pixels across will fill the width if C=32 or C=40, but not if C=30.

The number of character rows available is a function of the device (phone, tablet, PC) and its screen aspect ratio. The SimpleHMI function in SPLat/PC attempts to estimate this for a typical Android device, but it is impossible to be 100% sure it will always be correct due for example to different versions of Android and manufacturer-specific attributes.

An Event is raised when the device acknowledges this command. Your SPLat program should use that to trigger a re-draw of the screen (the screen is not redrawn automatically).

Example:

# HMI ScreenWidthEvent(40, evSetWidth)


# SetBGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android, Windows (except Alpha)

Sets the background colour for future display hash functions to the colour specified by Red, Green and Blue components using the RGB colour model. Legal values for each are 0 to 255. The 4th parameter, Alpha, is optional. It controls the transparency. Alpha = 0 gives total transparency, Alpha = 255 makes it totally opaque. Alpha is not implemented in the Windows SimpleHMI client.

Common colours are:

(255, 255, 255) | White
(255, 0, 0) | Red
(0, 255 ,0) | Green
(0, 0, 255) | Blue
(0, 0, 0) | Black
(128, 128, 128) | Medium grey

Examples:

#	HMI SetFGColour(255, 0, 0) SetBGColour(0, 0, 0) Print(" Hello World ")

will display the message in red letters on a black background.

#	HMI SetFGColour(255, 0, 0) SetBGColour(0, 0, 0, 0) Print(" Hello World ")

will display the message in red letters on a transparent background.

You may also use the keywords SetBGColour, BGColourRGB, BGColorRGB

See also SetFGColor, SetColors


# SetCursor(r|y:Row, c|x:Col) hash function

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android, Windows

Sets the cursor for any subsequent Text to the specified row and column. Row and column are in multiples of the default character size. They both start at 0, so the top lefthand corner of the screen is (0, 0)

In addition to being simple numbers, the row/column coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a row specifier | As a column specifier
5 | Row number 5 from the top, counting from 0. This can be a fractional number such as 5.5 | Column number 5 from the left, counting from 0
-5 | Row 5 from the bottom, counting from 0. This can be a fractional number such as -5.25 | Column 5 from the right, counting from 0
0 | The top character row | The left-most character column
-0 | The bottom character row | The right-most character column
0.25 | One quarter of (0.25 times) the screen height from the top. Fractional number between -1 and +1 are interpreted as a fraction of the screen. | One quarter of the screen width from the left.
-0.25 | One quarter of the screen height from the bottom. | One quarter of the screen width from the right.
C | The top-bottom centre of the screen. | The left-right centre of the screen.
C+5 | Five character rows down from the centre | Five character widths right from the centre.
C-5 | Five character rows up from the centre | Five character width left from the centre.
C+.25 | One quarter character row down from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | One quarter character width right from the centre.
C-3.25 | Three and a quarter character rows up from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | Three and a quarter character widths left from the centre.

Example:

# HMI Cursor(r:0, c:-3) Print(f(fCounter,4,0))

Display fCounter in the top righthand corner in a field 4 characters wide, with no decimal places . Remember, -0 gives the far right character position, -1 the second character in (space for 2 characters), etc.


# SetColors(f:FGColor, b:BGColor) hash function

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android

Sets the screen foreground colour and/or background colour in a single function.

FGColor is the foreground colour in ARGB-Hex format.

BGColor is the background colour in ARGB-Hex format.

You may also spell the keyword SetColours (UK English).

See also SetFGColor, SetBGColor


# SetFGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: HMi430, Android, Windows (except Alpha)

Sets the foreground (text) colour for future display hash functions to the colour specified by Red, Green and Blue components using the RGB colour model. Legal values for each are 0 to 255. The 4th parameter, Alpha, is optional. It controls the transparency. Alpha = 0 gives total transparency, Alpha = 255 makes it totally opaque. Alpha is not implemented in the Windows SimpleHMI client.

Common colours are:

(255, 255, 255) | White
(255, 0, 0) | Red
(0, 255 ,0) | Green
(0, 0, 255) | Blue
(0, 0, 0) | Black
(128, 128, 128) | Medium grey

Example:

#	HMI SetFGColour(r:0, g:0, b:255) SetBGColour(255, 0, 0) Print(" Hello World ")

will display the message in blue letters on a red background.

#	HMI SetFGColour(0, 0, 255) SetBGColour(255, 0, 0, 100) Print(" Hello World ")

will display the message in blue letters on a semi-transparent red background.

Hint: Colours can be defined using #EQU, to allow easy global changes. Example:

clrRED        #EQU     "255, 0, 0"
clrGREEN      #EQU     "0, 255, 0"
clrBLUE       #EQU     "0, 0, 255"
clrMIDGREY    #EQU     "128, 128, 128"
clrBackground #EQU     clrMIDGREY
...
# HMI SetFGColour(clrRED) SetBGColour(clrBackground)

You may also use the keywords SetFGColour, FGColourRGB, FGColorRGB

See also SetBGColor, SetColors


# SetImagePath(PathName)

Sets the file path (folder) in the display device where image files will be taken from.

Implemented on the following platforms: Android

The PathName must the the folder name relative to the top level (root) of the device's "external" storage, which generally means a plug-in SD card. In Android devices, when you connect to a PC via USB, and open the device for file transfers, that is where you end up.

If your program accesses images, but you don't specify a path, the default path will be used. The default path is SimpleHMI/appimages.

Note: Path and file names in Android are case-sensitive

This is what the file path looks like in Windows 7 Explorer on a Nexus 4 'phone running Android 4.4.4. ...

The Nexus 4 doesn't have external storage, i.e. a plug-in SD card.

... and this is what it looks like on an older HTC Desire C running Android 4.0.3 ...

This 'phone actually appears in My Computer as two "drives" when "mounted as a disk drive". The lefthand one in the image below is the phone's internal storage. The one we want is the righthand one, which represents a plugged in SD card.

Clearly you will need to explore accessing files on your device via a USB connection to you computer. If you use pictures taken on the phone, you may have some success creating and manipulating folders using an Android app like ES File Explorer.

We can not provide support on how to use Android.

Example:

#HMI SetImagePath("myappfiles/images")   ;No leading or trailing slashes!

Just to re-state what's above: Setting the path makes sense only when the path exists on the Android device and contains the desired image file(s)

See also: Reset(BGImage)


# SetRTCEvent({EventHandler}) hash function

Valid for use with hash command: HMI

Implemented on the following platforms: Android, Windows

This does two things:

  1. Generates the "under-the-hood" code that allows the SPLat board to receive Real Time Clock (RTC) date and time updates from an external SimpleHMI device, via serial or Bluetooth.
  2. Optionally lets you specify a handler to be run when the SimpleHMI device sends an RTC date and time update.

EventHandler is the name of your event handler. You don't have to have an event handler, because the actual updating of the board's RTC is automatic. If you have an event handler, it would be to do something like update the display. If you don't want an event handler you must still have the SetRTCEvent, but without naming a handler, e.g.

# HMI SetRTCEvent()

SetRTCEvent only makes sense if the SPLat controller you are programming has a Real Time Clock (RTC). There's a fully worked example online here.

See Also GetRTC, Open_Serial, User


# SpeakText (t:Text{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

Valid for use with hash command: HMI Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: Android, Windows (altered functionality).

Speaks out loud the supplied Text. See OldSpeakNumb for speaking numbers. Does not speak out loud in Windows; instead it shows a dismissible text box.

Language is the language to be spoken, and is specified as two lower case letters. Language codes. The target language must be installed in the Android device.

Country is the country specific variant ("dialect"). This is specified as two upper case letters. Country codes.

ClearQueue, if a non-zero number will result in any previously queued text being deleted. Otherwise the text for successive Speak functions is queued up.

Any of the arguments can be empty (with a necessary comma). If language/country are never specified the default setting on the Android device will apply.

Example:

The following program contains 3 calls to SpeakText(). The first call, during initialisation, sets language and country, with no text. The second call, in the evHelloWorld event handler subroutine has no ClearQueue flag. The third call has ClearQueue set (non-zero). This results in the spoken message "stomping on" any other speech that's in train. The example also illustrates the use of the OldSpeakNumb hash function. The program is written for an MS121 with a BT-TTL5 Bluetooth adaptor.

#       Open_Serial User(38400,8,N)
        GoSub           HMI_Connect    ;Paint the initial screen
#       HMI ConnectEvent(HMI_Connect)  ;Declare the handler for the Connected event
#       HMI SpeakText(t:"", L:"en", c:"AU",) ;Set the language/country for speech
        LaunchTask      HeartBeat      ;Blink an output to show we are alive
        LaunchTask      Crash          ;Catch an input, crash to get back control of serial port
        RunTasksForever

;Connected event: Paint the screen

HMI_Connect:

# HMI Reset() Cls() HideAllButtons()

# HMI ButtonEvent(, 0.25, 0.2, 3, 0.6, "Hello World",  evHelloWorld)

# HMI ButtonEvent(, 0.5, 0.2, 3, 0.6, "How are you?",  evHowRU)

# HMI ButtonEvent(, 0.75, 0.2, 3, 0.6, "Count",  evCount)

        Return

evHelloWorld:

#       HMI SpeakText(t:"Hello world")

        Return

evHowRU:

#       HMI SpeakText("How are you?",,,1)        ;Will pre-empt previous speeches.

        Return

evCount:

#       HMI SpeakText(t:"Count is ") SpeakNumb(f(*fCount, 5, 1))

        fRecallW        fCount

        fLoadQ          0.1

        fAdd

        fStore          fCount

        Return

fCount defFLOAT

********************* Minor stuff ***************************************************

;Blink an output to signal the program is running

HeartBeat:

        On              8

        Pause           1

        Off             8

        Pause           25

        GoTo            HeartBeat

;Deliberately crash the program to get back control of the serial port, so SPLat/PC can connect.

Crash:  WaitOnK         12

Here    GoSub           Here


# SpeakNumb(n:Number{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

Valid for use with hash command: HMI. Arguments may be specified as Name:Value Pairs

Implemented on the following platforms: Android, Windows (limited support)

This uses the Android text to speech engine to say a number out loud.

Number is the number to be spoken. It can be a literal number or a numeric formatting function.

Language is the language to be spoken, and is specified as two lower case letters. Language codes. The target language must be installed in the Android device.

Country is the country specific variant ("dialect"). This is specified as two upper case letters. Country codes.

ClearQueue, if a non-zero number, will result in any previously queued text being deleted. Otherwise the text for successive Speak functions is queued up. Hitting the Android device with too much speech data, too rapidly, could cause problems.

Example: The following tiny program will count out loud on an Android device.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

HMIPort         EQU     252                ;Give serial port 252 a name

#Open_Serial Port(HMIPort) User(9600,8,N)  ;Open serial port 252, set User protocol

# HMI Port(HMIPort)                        ;Assign the serial port to the HMI function

        LaunchTask      tskCount           ;Launch the counting task

        RunTasksForever                    ;Start MultiTrack

;The counting task.

;NOTE: 32 bit boards like the EC1 preserve registers, like W, for individual tasks.

;      That's why this program can keep the counter in W. That would not work with

;      an older 8-bit controller like MS121. See the counter task in SpeakText, which

;      has to keep the counter in a RAM variable.

tskCount:

        fLoadW          0                  ;Initialise the counter - using register U

        fLoadQ          1                  ;Count up by this amount

Count1:

        fAdd                               ;Count up

        #HMI SpeakNumb(n:f(=w, 5, 0))      ;Speak

        Pause           200                ;Delay

        GoTo            Count1             ;Repeat


# Print(Arg1{, Arg2 ...}) hash function

Valid for use with hash commands: HMI, HMI_Trace and HMI_Log (on the platforms that support those respective hash commands)

Implemented on the following platforms: HMi430, Android, Windows.

The Print hash function can take almost as many comma-separated Arguments as you like - the limit is set by the size of the communications buffer in most platforms, which is 255 bytes. However, it's sensible to not make the argument list too long.

The arguments can be either quoted text strings or formatting functions.

Handling of non-printable character codes

Non-printable character codes can be sent to the display using the literal formatting function. The result will depend entirely on the platform. For example, SimpleHMI in Android does not respond to carriage return or line feed characters, (nor does it word-wrap). The HMI_Log display in Windows does respond to carriage return and line feed characters.

Example: A very simple clock display on the HMI430. The time is formatted using the byte formatting function with the "2" format specifier..

;Dead simple clock on HMI430
Loop:
        RTCReadTime             ;Read RTC to HH, MM and SS registers
        #HMI SetCursor(0, 0) Print("Hello, the time is ", b(=hh, 2), ":", b(=mm, 2), ":", b(=ss, 2))
        Pause           10      ;Update every 100mS
        GoTo            Loop    ;Repeat forever


# uDispXFW() hash function

Valid for use with hash commands: HMI, HMI_Trace, and HMI_Log

This function will cause the 8-bit number in X to be output in unsigned decimal format in a variable width field. The formatting is identical to OBLCD_UDecDispXFW. This means the displayed number will be right justified in a fixed width field of three character spaces.


# HMI_Log hash command

The HMI_Log hash command accesses a versatile data logging capability within SimpleHMI. You can easily generate log files in any text format, for example plain text or CSV. CSV is a defacto standard for importing into spreadsheets and data bases.

Note that in SPLat/PC the SIO/SimpleHMI window must be active for logging to take place. A file path (folder name) must be specified for the log file, in the Settings tab of the SIO window. The file name itself is embedded in the SPLat program.

In This Section

# fDispW(f, p) hash function

# FileOpen(FileName, Mode) hash function

# FloatVar(fArg, f, p) hash function

# HexDispX() hash function

# HexVar(bArg) hash function

# HHMMSS() hash function

# LogWrite() hash function

# NewFile(FileName) hash function

# Print(Arg1{, Arg2 ...}) hash function

# uDispXFW() hash function

# uDispXVW() hash function

# YYMMDD() hash function


# FileOpen(FileName, Mode) hash function

Valid for use with hash command: HMI_Log

This function serves multiple purposes:

  1. Selecting the base of a file name to be used for subsequent logging operations.
  2. Creating an initial file with the given name.
  3. Setting the mode to use with the file name.
  4. Subsequently, switching between log files.

The first time a file is to be opened in a session of SimpleHMI, the Mode must be specified. Any subsequent uses of the Mode argument are unnecessary, and ignored. You can have up to 20 log files on the go at once. Any log data from the SPLat (host) will be saved to the file last mentioned in a FileOpen, thus the FileOpen function serves to switch between files. You may for example use one file to log temperature every second and another to log manual interventions by an Enduser.

The safest policy is to do a FileOpen just before each write to a file, and also during the Connect event handler, and to include the mode every time.

FileName

The FileName should be a simple file name, without a directory/folder path. It may optionally contain a file name extension. Examples are:

MyFile.txt

MyFile

The first of those includes an extension, which will be used when the file is written. The second contains no extension. SimpleHMI will default it to .log.

Note: Windows is insensitive to case, so MyFile, myfile and MYfIle are all equivalent. The Android file system is case sensitive. The SimpleHMI shares code between Windows and Android, and is internally case-insensitive. If you vary the case between references, the results in Android could be unpredictable.

Mode

The mode controls the automatic generation of date codes and sequence numbers as part of the file name written to disk/SD card according to the following table:.

Mode | Description | Behaviour when SimpleHMI is first started | Effect of NewFile(FileName) hash function
0 | Plain. The base name is used as-is. The base name may not include the character pairs; _- or -_ | Data is appended to any existing file with this name. | The existing file will be overwritten on the next LogWrite to this file.
1 | Dated. A date stamp is automatically included in the file name. The form is; -_yymmdd, e.g. MyFile-_120324.log for March 3rd 2012. | Data is appended to any existing file with this name. | If an existing file of the same base name has today's date, it will be overwritten on the next LogWrite to this file, and old data will be lost. Otherwise a new file will be created with today's date.
2 | Numbered. A sequence number is automatically included in the file name. The form is; _-nnnnn, e.g. MyFile_-00023.log | On the next LogWrite to this file base name the number will be incremented. | On the next LogWrite to this file base name the number will be incremented.
3 | Dated and numbered, for example; MyFile-_120324_-00023.log | If the existing file of the same base name has today's date, then on the next LogWrite to this file base name the number will be incremented. | If the existing file of the same base name has today's date, then on the next LogWrite to this file base name the number will be incremented.

The logging directory/folder

The folder used to save log files is not controller from the SPLat.

In Android the log files are always written to a default directory. When you plug the Android device into a computer via USB the files will be in a directory named SimpleHMI/logging. No user action, short of removing the SD card from the device, can stop logging.

In Windows the enduser must select a folder for log files to be stored in. If no folder has been selected, SimpleHMI will prompt the enduser for one, just once. The selected folder is remembered between sessions, and my be changed in the Settings tab. If none has been selected the system will ask the enduser to select a folder. If no folder is selected, logging is impossible and log data is ignored.


# HHMMSS() hash function

Valid for use with hash command: HMI_Log

Writes the current time to the currently open log file, in the format HHMMSS (24-clock).. The time is the time in the device running SimpleHMI


# LogWrite() hash function

Valid for use with hash command: HMI_Log

Causes log data to actually get written to Disk/SD card. Data from the SPLat is accumulated within the SimpleHMI client until a LogWrite is executed. At that time:

  1. The decision is made as to whether to create a new file, according to the rules and any prior NewFile.
  2. The file is opened and the data is written or appended to the file. Each record is terminated in '0D '0A (CR/LF), Windows style.
  3. The file is closed.


# NewFile(FileName) hash function

Valid for use with hash command: HMI_Log

Causes a new file to be created according to the rules tabulated under FileOpen. Note that FileOpen is enough to create the initial instance of a file name. NewFile is used to either increment the sequence number on an existing numbered file or overwrite an existing non-numbered file.


# YYMMDD() hash function

Valid for use with hash command: HMI_Log

Writes the current date to the currently open log file, in the format YYMMDD. The date is the date in the device running SimpleHMI


# HMI_Trace hash command

The SimpleHMI function provided in SPLat/PC includes a trace feature, intended primarily as a tool for debugging of your SPLat program. This is not supported in the Android version of SimpleHMI, because it doesn't fit so well with the Android use cases.

The regular SimpleHMI function lets you manipulate the screen layout, and have for example a spot on the screen that shows the latest temperature reading. Each time the temperature is updated the previous value is lost. The trace function, by contrast, provides a scrolling screen that gives a running record of what has happened, so it could show the last 10, or 100, or 1000 temperature readings. The trace function also lets you save data to disk for later review.

The trace screen only appears if you click the Show Trace button.

In This Section

# fDispW(f, p) hash function

# FloatVar(fArg, f, p) hash function

# HexDispX() hash function

# HexVar(bArg) hash function

# NL() hash function

# Print(Arg1{, Arg2 ...}) hash function

# TraceOff() hash function

# TraceOn() hash function

# uDispXFW() hash function

# uDispXVW() hash function


# NL() hash function

Valid for use with hash command: HMI_Trace

Forces the trace screen to a new line.

The SimpleHMI main screen does not respond to carriage return or line feed characters, nor does it word-wrap. If any text runs off the edge of the screen it will simply not show.

The trace display does respond to carriage return and line feed characters, and does not word wrap. However, the screen can be manually scrolled horizontally and vertically.


# TraceOff() hash function

Valid for use with hash command: HMI_Trace

All # Hash_Trace hash commands below this point in the source file will be ignored. The default is On.


# TraceOn() hash function

Valid for use with hash command: HMI_Trace

All # Hash_Trace hash commands below this point in the source file will be enabled. The default is On.

Example:

# HMI_Trace TraceOn()
# HMI_Trace Print("This should show")
# HMI_Trace TraceOff()
# HMI_Trace Print("This should NOT show")
# HMI_Trace TraceOn()
# HMI_Trace Print("This should show")

TraceOn/TraceOff can also be used in a single line and work as expected.

# HMI_Trace TraceOn() Print("This should show") TraceOff() Print(" This should NOT show") TraceOn() Print(" This should show") NL()


# Include hash command

The Include hash command lets you include one SPLat source files within another. The effect is the same as manually copying and pasting the contents of one file into another.

Example:

Suppose you have a file named washington.spt that looks like this:

;----- Important stuff happens here ----
Potus:	  defBYTE
No1600:    Recall    Potus
           Return

Then in your main file you have:

....
; Find the value of Potus
           GoSub     No1600
...
...
# Include File(washington.spt)

When your main file is translated by SPLat/PC, the line

# Include File(washington.spt)

gets replaced by the entire contents of the file washington.spt . It is as if you had copied and pasted one file into another. You effectively end up with:

....

; Find the value of Potus

           GoSub     No1600

...

...

;# Include File(washington.spt)

;----- Important stuff happens here ----

Potus:	  defBYTE

No1600:    Recall    Potus

           Return

This is a very useful way of managing projects, and of keeping you favourite routines and code snippets in a library.

You can have any number of Include hash commands in a file. You can even have includes within included files. Included files can be nested 10 deep. You cannot have recursive inclusions, such as a file including itself or two files that include each other as this will violate the allowed nesting depth.

The Include hash command gives you a powerful mechanism for building and using libraries.

You cannot single step or simulate any hash command, including the Include hash command. You can however use the menu item File>Pre-process to generate a file where all the hash commands have been processed and the resultant code has been generated.

The Include hash command is available in SPLat/PC Build 181 and later.

In This Section
# File(FileName) hash function


# File(FileName) hash function

Valid for use with hash command: Include

Use this to specify the file that is to be included.

Restriction: If file name includes spaces it needs to be enclosed in quotes.


# Open_Serial hash command

The Open_Serial hash command gives you a convenient way of initialising the serial port without having to manually build Communications Control Blocks (CCB) in NVEM. OPen_Serial "takes" one of a number of hash functions, each hash function being specific to a communications protocol.

The first time an Open_Serial hash command is executed in a program, the program will stall until 10 seconds from startup. It is perfectly possible to "re-open" the serial port with different settings, and the behaviour will change immediately.

Example:

# Open_Serial User(38400,8,N)

The above hash command will open the serial port at 38400, with 8 data bits and no parity, and invoke the User protocol. "User protocol" means the port can be used to send and receive any arbitrary data (as distinct from, say, ModBus, which has its own very specific data formats).

The available hash functions are listed in this section.

In This Section

# ModBusMaster(BaudRate, Parity, MessageGap) hash function

# ModBusSlave(BaudRate, Parity, Address, MessageGap) hash function

# Port(p) hash function

# SPLatLink(BaudRate) hash function

# User(BaudRate, Bits, Parity) hash function


# ModBusMaster(BaudRate, Parity, MessageGap) hash function

Valid for use with hash command: Open_Serial

This hash function sets the serial port up for use with the ModBusRTU master. Once initiated and configured, the SPLat controller Firmware takes care of all the low level communications activity. ModBus documentation.

Parameters are:

BaudRate | The baudrate to be used. Legal values are 300, 600, 1200, 2400, 4800, 9600, 19200, and 38400
Parity | The parity to be added to characters sent, and checked on characters received. Legal values are: N - No parity bit; E - Even parity; O - Odd parity
MessageGap | See description here. Set to 0 for the default.

You should set these to match the device you are communicating with.


# ModBusSlave(BaudRate, Parity, Address, MessageGap) hash function

Valid for use with hash command: Open_Serial

This hash function sets the serial port up for use with the ModBusRTU slave. Once initiated and configured, the SPLat controller Firmware takes care of all the low level communications activity. ModBus documentation.

Parameters are:

BaudRate | The baudrate to be used. Legal values are 300, 600, 1200, 2400, 4800, 9600, 19200, and 38400
Parity | The parity to be added to characters sent, and checked on characters received. Legal values are: N - No parity bit; E - Even parity; O - Odd parity
Address | The ModBus slave address of the SPLat controller. Legal values are 1 through 247
MessageGap | See description here. Set to 0 for the default.

You should set these to match the device you are communicating with.


# SPLatLink(BaudRate) hash function

Valid for use with hash command: Open_Serial

This hash function sets the serial port up for use with SPLatLink protocol. SPLatLink is SPLat's native protocol, used for interacting with the controller during programming.

Parameters are:

BaudRate

The baudrate to be used. Legal values are 9600 and 38400

This hash function can be useful for programmatically switching the controller back to SPLatLink when it needs to re-connect to SPLat/PC, during program development.


# User(BaudRate, Bits, Parity) hash function

Valid for use with hash command: Open_Serial

This hash function sets the serial port up for use with the "user protocol". The user protocol is whatever you program yourself.

Parameters are:

BaudRate | The baudrate to be used. Legal values are 300, 600, 1200, 2400, 4800, 9600, 19200, and 38400
Bits | The number of data bits in each character. Legal values are 7 and 8
Parity | The parity to be added to characters sent and checked on characters received. Legal values are: N - No parity bit; E - Even parity; O - Odd parity

You should set these to match the device you are communicating with.


# Thermistor hash command

The Thermistor hash command makes it very easy to program for NTC thermistor temperature sensors.

Do not confuse thermistors with thermocouples or RTD sensors. The Thermistor hash command applies to NTC (negative temperature coefficient) thermistors, and nothing else.

Circuit configuration

The Thermistor hash command assumes the following circuit configuration, which is used in a large number of standard and custom SPLat controllers.

Example:

The following will return the temperature in ºC of a thermistor connected to analog input 0. The result will be in W. The characteristics of the thermistor and actual circuit values are defined by the hash functions that follow the Thermistor hash command.

       fAnIn    0    ;Raw thermistor voltage to W
# Thermistor Params(10000, 3840) Theta(1) Feed(5.6, 5, 50000) RangeC(-25, 50) Order(5) Display(10) ; temp now in W

The various hash functions are explained in the rest of this section.

How it works

When SPLat/PC encounters the Thermistor hash command during program translation, it uses the values supplied in the associated hash functions to calculate a table of raw analog reading versus temperature. It then processes the table to produce a best fit polynomial, and stores the polynomial coefficients in an NVEM0 table. When your SPLat program later wants to convert a raw analog thermistor reading to temperature, the raw reading is "fed into" the polynomial and converted to temperature. In effect, this process automates the older procedure given in the thermistor temperature measurement tutorial.

The result screen and sandbox

If the Thermistor hash command line includes a Display hash function, SPLat/PC will pop up a special window that shows the results of its internal calculations. This window includes a sandbox function, that lets you experiment with different values and generate a modified Thermistor command line.

Selecting a thermistor

There are no hard and fast rules for thermistor selection, but here are some pointers.

  • Thermistors are specified in terms of R25 and Beta. R25 is the resistance at 25°C. Beta is a number that defines how much the resistance changes with temperature. Typical Beta values are around 4000. You can't do much about Beta but you can usually select an R25 value.

    Many thermistor families come in a range of R25 values.Typical values are 1K, 2K, 5K, 10K, 20K, 50K and 100K.

  • The higher the temperatures you want to measure, the higher the R25 value will need to be to get optimum performance, and visa versa. For example, if you are interested in temperatures around 0°C, a 1K or 2K thermistor will be appropriate. Around room temperature a 10K unit will work well and for boiling water look at a 100K. The rule of thumb is that the thermistor resistance at the temperature of main interest should be about the same as the feed resistor Rfeed.
  • Thermistors come in many shapes and sizes. Some are designed for temperature measurement, while others are designed for other functions like circuit protection. (A common circuit protection use is inrush current limiting in power supplies). Obviously, you want one that's intended mainly for temperature sensing or measurement. You also want one you can "live with" in terms of mounting, encapsulation etc. Some companies offer thermistors already made up into probes (click here, if you are online, to search), while others sell the bare component. Betatherm is a typical company that specializes in quality thermistors for temperature measurement, but there are many others. Temperatures.com provides many more suppliers plus a lot of additional information about temperature measurement.

We are in no way associated with any of the above referenced resources and take no responsibility for their accuracy.

  • Thermistors come in many accuracy grades. Usually they are specified in terms of the R25 tolerance, with typical specs being 0.5%, 1%, 2% or sometimes 5%. As a first approximation the temperature accuracy of the thermistor at 25°C will be half the percentage tolerance, meaning a 2% thermistor will be about 1°C accurate at 25°C. That compares very well with semiconductor temperature sensors. For example, the popular LM35 has a 2.5°C accuracy for the low grade part, and 1°C for the premium part. Betatherm and several other specialist companies have thermistors with an initial accuracy of 0.05°C at 25°C

Don't equate the thermistor accuracy with the final measurement accuracy you will achieve! There are many other sources of error that cannot be controlled as easily as just paying out a few more dollars for the thermistor. The main benefit of buying a very high accuracy thermistor is interchangeability.

The thermistor hash command does not set the thermistor drive voltage. On the MMi202 and SL100 you need to initialise the thermistor drive voltage yourself.

MMi202 thermistor drive voltage SL100 thermistor drive voltage (Look near the bottom of the respective pages)

In This Section

# Display(Bits) hash function

# Feed(Vfeed, Vfs, Rfeed) hash function

# Params(R25, Beta) hash function

# Order(o) hash function

# RangeC(Tmin, Tmax) hash function

# Theta(Rt) hash function


# Display(Bits) hash function

Valid for use with hash command: Thermistor

The Display hash function does two things:

  1. When SPLat/PC processes the Thermistor hash command, Display causes it to show a pop-up window that shows you the results of the thermistor calculations.
  2. The Bits argument tells SPLat/PC the resolution of your analog input, so it can calculate the temperature final resolution. Most SPLat controllers have an analog input resolution of 10 bits. See your controller data for specifics.

Display is totally optional. Hint: Place it last on the Thermistor line, so it is easy to Comment out.


# Feed(Vfeed, Vfs, Rfeed) hash function

Valid for use with hash command: Thermistor

This hash function defines the electrical conditions applied to the thermistor

Vfeed is the feed voltage. Unless you are doing something out of the ordinary, this will be sourced by the SPLat controller or an add-on SPice board either from an analog output or as part of a dedicated thermistor port. See the data for the SPLat board you are connecting the thermistor to.

Rfeed is the feed resistor, usually also determined by the board. Typical values are 10000 or 10250.

Vfs is the full scale voltage range of the analog input. Again, documented with the specific board, though usually 5V.

The thermistor hash command does not set the thermistor drive voltage. On the MMi202 and SL100 you need to initialise the thermistor drive voltage yourself.

MMi202 thermistor drive voltage SL100 thermistor drive voltage (Look near the bottom of the respective pages)


# Params(R25, Beta) hash function

Valid for use with hash command: Thermistor

The resistance versus temperature response of an NTC thermistor can be mathematically approximated by using 2 parameters in an equation. We take care of the equation; you just have to supply the two parameters.

The parameters are R25 and Beta. R25 is the nominal resistance at 25ºC, in Ohms. Beta is a measure of the non-linear temperature curve. Sometimes Beta is given as Beta(25/85) or for some other temperature range. Here's a snippet of a typical data sheet:

If you were for example using the 5th thermistor from the top in this data sheet, you would have

Params(10000, 3977)

Note that the data sheet gives Kilo-Ohms, whereas we want ohms. 1 Kilo-Ohm = 1000 Ohms.


# Order(o) hash function

Valid for use with hash command: Thermistor

This defines the "order" of polynomial that will be generated. The higher the order the closer the polynomial will get to the correct values, but the more time the SPLat will take performing the calculation. The maximum allowable order is 20, but 5 to 7 are usually as far as it ever pays to go. The Display hash function lets you explore different values.


# RangeC(Tmin, Tmax) hash function

Valid for use with hash command: Thermistor

This hash function defines the temperature range you wish to measure, in ºC. You should make this as narrow as possible while still covering the necessary range.


# Theta(Rt) hash function

Valid for use with hash command: Thermistor

Theta is a measure of the amount of self-heating in the thermistor. Because, unavoidably, there must be some current flowing through the thermistor, it will heat up. The heating up will cause it to give a slightly higher temperature reading than it should.

The Theta hash function allows for some compensation for self-heating. Note that it will never be dead accurate, and if mis-applied may do more harm than good. You don't have to use Theta.

The Rt parameter is the thermal resistance of the thermistor, in ºC/mW. For example, if Rt is 1ºC/mW, it means that if 1mW of heat is dissipated in the thermistor it will self-heat by 1ºC. If 1.23mW is dissipated it will self-heat by 1.23ºC, etc. The SPLat/PC is able to calculate the temperature rise for all measurement temperatures (it will vary) and compensate for it in the generated polynomial.

Thermistor data sheets may specify thermal resistance, dissipation factor, or neither (in the latter case you can't really compensate for it).

Dissipation factor is the inverse of thermal resistance. Here's an example from a data sheet:

This is given in units of mW/K. The K simply denotes Kelvin, which for this purpose is the same as Celsius. So you can read that as 2.2mW/ºC, which means it takes 2.2mW of dissipation to raise the temperature by 1ºC. Now, the Theta hash function wants the inverse figure, so the number we want is 1/2.2 or 0.455ºC/mW.

(One of the reasons we did it that way around was to allow you to specify 0 as the parameter value, and not hit divide by zero problems later)

Theta can be a slippery customer!

The thermal resistance of a thermistor will vary wildly with its operating environment. If you put a fan on it, it will get rid of heat more easily and therefore not self-heat so much (a fact than can be used to measure air flow with two identical thermistors dissipating different amounts of heat). If you put the thermistor in water or oil, the heat will also be removed more quickly. If you attach a heatsink to it it will self-heat less. Even a blob of Blu-Tack will act as a heatsink, because it increases the surface area. A consequence of the last observation is that if you package a thermistor bead in, say, a brass protective ferrule or immersion probe, you will also alter (almost certainly lower) its thermal resistance.

In conclusion, the figure provided in a data sheet is useful only if you use the thermistor without additional packaging, and in still air.

Some recent custom SPLat products use pulsing of the thermistor drive to minimize self-heating. Please consult us about compensating if you are using such a board.


Index register instructions

The index register allows you to select inputs, outputs, RAM and NVEM dynamically. This is especially useful if you are writing a program that will work identically on several "channels", say if you are making a 4-channel timer or lighting controller.

From December 2005 (dialect 16) the index register I is supplemented by the J register. J is available inside MultiTrack tasks and makes programming of multichannel functions incredibly easy. Addressing via J is call jndexing.

This section contains instructions that can reference the index register I. Some of the instructions directly manipulate I. Others use the index register as an offset on the I/O, RAM or NVEM address in the instruction. When you are using the index register as an offset, you must add the letter 'i' in front of the instruction key word to get this indexed mode. For example, Recall becomes iRecall. Before being added to the base address I is multiplied by a constant. That constant is derived from the type of memory access. For example, in floating point instructions ifStore, ifRecallW and ifRecallQ, the index register is multiplied by 4, so it can be used to index into an array. For simple byte access instructions like iRecall it is multiplied by 1.

From dialect 16 every memory reference and I/O instruction can be indexed using the IasJ: precode.

In indexed semaphore instructions, the index register is added to the base address argument (not the bit number). With jndexed addressing (using J) it is the opposite: the bit number gets offset.

Those instructions that allow indexing have an asterisk in their topic title. The asterisk is placed after the instruction argument that gets indexed.

Those instructions that allow jndexing from dialect 16 have a plus sign in their topic title. The plus sign is placed after the instruction argument that gets jndexed.

From dialect 23 some instructions use automatic indexing.

When the instruction is executed the index register is automatically incremented.See the relevant instruction description to find the amount the index register is incremented by.

Automatic indexing instructions have a hash in their topic title. The hash is placed after the instruction argument that gets indexed.

In This Section

ClrS ss+,bb* [D>=12]

DecI

fRecallQ mm*+

fRecallW mm*+

fStore mm*+

GoIfINZ LLLL

GoIfIZ LLLL

iiChrPrintX dest# [D>=23]

iifPrintWFW dest#,f,p [D>=23]

iifPrintWVW dest#,f,p [D>=23]

iiHexPrintX dest# [D>=23]

iiPrintFill dest#,count,value [D>=23]

iiPrintNVText dest# [D>=27]

iiPrintText dest#,text-string [D >= 23]

iisPrintXFW dest# [D>=23]

iisPrintXVW dest# [D>=23]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]

IncI

Input ii*+

InputF ii*+

InputK ii*+

InputO ii*+

ItoX

LoadI nn

Off oo*+

On oo*+

Output oo*+

NotS ss+,bb* [D>=12]

NVfReadQ nn*+ [D>=15]

NVfReadW nn*+ [D>=15]

NVfWriteW nn*+ [D>=15]

NVPopByte nn*+ [D>=15]

NVPushByte nn*+ [D>=15]

Recall mm*+

RecallS ss+,bb* [D>=12]

SetS ss+,bb* [D>=12]

ShadowClear mm*+,cc

ShadowRead mm*+,cc

ShadowWrite mm*+,cc

Store mm*+

StoreS ss+,bb* [D>=12]

XtoI


ClrS ss+,bb* [D>=12]

Clear semaphore ss, counting from base address bb. The base address bb is optional; if omitted it is taken to be 0.

For more details see the semaphore tutorial and the use of semaphores in the MultiTrack tutorial

This instruction can be index by adding an 'i' in front of the instruction keyword. Indexing adds the contents of the index register I to the base address bb. That means it modifies the byte base address, not the bit address.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

The following is illegal: IasJ:iClrS

Dialect restriction: Available only in boards with dialect 12 or greater.


Input ii*+

Pushes the state of the nominated input number ii to X in Boolean form. If the input has a closed contact X becomes True, otherwise X becomes False.

This instruction uses debounced inputs. Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches. If you need a faster response you can use the InputF (fast input) instruction, but you will get no protection from contact bounce.

* This instruction can also use the Index Register by adding i in front of the instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=6

See also Output, InputF InputK InputO InputFM


InputF ii*+

Pushes the state of the nominated input number ii to X in Boolean form. If the input has a closed contact X becomes True, otherwise X becomes False.

This is a fast input instruction which bypasses the debounce mechanism used for the Input instruction. InputF tests the state of the input line directly, and can be used for detecting rapid input changes. Where speed is not important (i.e. a 10-20mS response time is OK), you should use the debounced Input instruction..

* This instruction can also use the Index Register by adding i in front of the instruction. Click here for a list of indexed instructions.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: The InputF instruction is not implemented in SP108 hardware boards with Firmware Version 1.0.

See also Output, Input


InputK ii*+

This is a special input instruction implemented for SPLat boards with on-board Push button switches, such as the MMi99. It is also available on normal digital inputs on most boards.

InputK provides an "edge-triggered" input function. It will return a True result if input ii has changed from off to on at any time since the last InputK was executed on the particular input. In terms of a Push button input, if the button is pressed momentarily, the first subsequent InputK will return a True value but any later InputK will not, until the button is released and pressed once more.

The result of the InputK is Pushed into X in Boolean form.

On boards that incorporate a beeper, the InputK instruction results in a short beep whenever it returns a true result.

This instruction uses debounced inputs. (The high speed counting input may be used to capture faster events) Debounce adds a delay of 8 to 20mS to the input response, in order to reject contact bounce (chattering) in mechanical input switches.

If the InputK instruction is executed on an input that does not support it, the board treats it as a normal Input instruction.

* This instruction can also use the Index Register by adding i in front of the instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction. InputK is implemented only in dialects 3 or later.

See also Output, Input, InputF, KBeepOn, KBeepOff WaitOnK GoIfInK


InputO ii*+

(Read back an output)

Pushes the state of the nominated output number ii to X in Boolean form. Note that the value returned is the value that has been most recently set on the output by an On, Off, Output or OutputM instruction, as opposed to being a readback of the voltage at the output pin. This is particularly significant on SPLat boards with bidirectional I/O points, such as the OEM36.

If an output is blinking (see Blink and OutputB) the InputO instruction will return a True result.

* This instruction can also use the Index Register by adding i in front of the instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

This instruction will not readback an output ON condition resulting from an fPulse0 instruction.

Dialect restriction: The InputO instruction is not implemented in boards with dialect numbers less than 3.

See also Output, Input


ItoX

Pushes the contents of the Index Register into X

Dialect restriction: This instruction is not implemented on boards prior to dialect D=6

See also XtoI


LoadI nn

Loads the number nn into the Index register I. nn must lie between 0 and 255. No other registers are affected.

Dialect restriction: This instruction is not implemented on boards prior to dialect D=6

See also LoadX


Output oo*+

Copy contents of X to nominated output number oo as Boolean and Pop register stack. If X is initially 0 (False) the output is turned off. If X is initially nonzero (True) the output is turned on.

* This instruction can also use the Index Register by adding i in front of the instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See also Input InputO


NotS ss+,bb* [D>=12]

Complement (invert) the value of semaphore ss, counting from base address bb. True becomes False and False becomes True. The base address bb is optional; if omitted it is taken to be 0.

For more details see the semaphore tutorial.

This instruction can be index by adding an 'i' in front of the instruction keyword. Indexing adds the contents of the index register I to the base address bb. That means it modifies the byte base address, not the bit address.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: Available only in boards with dialect 12 or greater.


NVPopByte nn*+ [D>=15]

Pop X and write the byte to NVEM at Pointer + RecLen x RecNum + nn. Raise a fatal error if the transfer runs past the end of NVEM.

This instruction can also be used in index mode by using the keyword iNVPopByte. In that case the transfer is at NVEM address Pointer + RecLen x RecNum + nn + ii, where ii is the contents of the index register I.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVPushByte nn*+ [D>=15]

Get one byte from NVEM at Pointer + RecLen x RecNum + nn and push it into X. Raise a fatal error if the transfer runs past the end of NVEM.

This instruction can also be used in index mode by using the keyword iNVPushByte. In that case the transfer is at NVEM address Pointer + RecLen x RecNum + nn + ii, where ii is the contents of the index register I.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


Recall mm*+

Pushes the contents of memory location mm into X.

* This instruction can also use the Index Register by adding i in front of the instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See also Store. Do not confuse with LoadX


RecallS ss+,bb* [D>=12]

Push semaphore ss to X. The semaphore base address bb is optional; if omitted it is taken to be 0. The value placed in X will be the value in the byte containing the semaphore with all other bits set to zero. For example, if the semaphore is in bit position 4 in the byte, and it is set, then after the RecallS instruction X will contain %00010000.

Because SPLat interprets any non-zero value in X as True, this behaviour will give the correct response to a subsequent True/False test or simple Boolean operation. It may however give unpredictable results if a subsequent operation uses a masked (bit-wise) boolean instruction like AndM, i.e. operates on individual bits. If this is a risk then you can "normalize" the result by doing two NOT operations on it.

For more details see the semaphore tutorial.

This instruction can be index by adding an 'i' in front of the instruction keyword. Indexing adds the contents of the index register I to the base address bb. That means it modifies the byte base address, not the bit address.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode. jndexing of semaphore addresses adds the contents of J to the bit address ss, not to the byte address bb. See Multi-channel MultiTrack: Semaphore addressing with J and I

The following is illegal: IasJ:iRecallS

Dialect restriction: Available only in boards with dialect 12 or greater.


SetS ss+,bb* [D>=12]

Set semaphore ss, counting from base address bb. The base address bb is optional; if omitted it is taken to be 0.

For more details see the semaphore tutorial.

This instruction can be index by adding an 'i' in front of the instruction keyword. Indexing adds the contents of the index register I to the base address bb. That means it modifies the byte base address, not the bit address.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

The following is illegal: IasJ:iSetS

Dialect restriction: Available only in boards with dialect 12 or greater.


ShadowClear mm*+,cc

Clear (write 0 to) a region of cc bytes of non-volatile shadow memory starting at address (location number) mm. Data in the shadow memory is retained even when the power goes off.

The R register will be set to 0 if the clear operation is successful. It will be set to 1 if the clear fails.

Caution: If the start address mm plus the count cc add up to an address beyond the end of RAM, the board will generate a fatal runtime error and reset.

Notes (see the product documentation for your specific board for details):

  1. The shadow memory has a finite endurance. A shadow memory location will wear out after a certain number of write or clear operations.
  2. This instruction takes a comparatively long time. During that time the SPLat processor is doing nothing and may miss fast input and will not service timers.

This instruction can be used in indexed mode, i.e. iShadowClear.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Note that the index or jndex offset is I or J single bytes. This can produce unexpected results if you are clearing for example jndexed floating point data.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

Dialect exclusions: Not available in dialects before 10.

See also ShadowWrite ShadowRead


ShadowRead mm*+,cc

Read from non-volatile shadow memory to RAM. Start at address (location number) mm and read cc bytes. If the shadow memory has not been previously written to, it will contain 0.

Data in the shadow memory is retained even when the power goes off.

Caution: If the start address mm plus the count cc add up to an address beyond the end of RAM, the board will generate a fatal runtime error and reset.

This instruction can be used in indexed mode, i.e. iShadowRead.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Note that the index or jndex offset is I or J single bytes. This can produce unexpected results if you are reading for example jndexed floating point data.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

Dialect exclusions: Not available in dialects before 10.

See also ShadowWrite ShadowClear


ShadowWrite mm*+,cc

Write from RAM to non-volatile shadow memory. Start at RAM address (location number) mm and write cc bytes. Data in the shadow memory is retained even when the power goes off.

The R register will be set to 0 if the write operation is successful. It will be set to 1 if the write fails.

Caution: If the start address mm plus the count cc add up to an address beyond the end of RAM, the board will generate a fatal runtime error and reset.

Notes (see the product documentation for your specific board for details):

  1. The shadow memory has a finite endurance. A shadow memory location will wear out after a certain number of write or clear operations.
  2. This instruction takes a comparatively long time. During that time the SPLat processor is doing nothing and may miss fast inputs and will not service timers. It is up to you to design your program to ensure this does not cause problems!

This instruction can be used in indexed mode, i.e. iShadowWrite.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Note that the index or jndex offset is I or J single bytes. This can produce unexpected results if you are saving for example jndexed floating point data.

Shadow memory is not affected by downloading a new program to the controller. That means any user setup parameters (for example) will remain unchanged. That represents both an advantage and a risk. If you change your program or we change the way we allocate RAM (defBYTE etc) you could have problem, because suddenly your parameters are no longer at the expected addresses. You should therefore consider using absolute RAM addresses (mEQU) to allocate RAM to user parameters.

Dialect exclusions: Not available in dialects before 10.

See also ShadowRead ShadowClear


Store mm*+

Copies the contents of X to data memory location mm and Pops the register stack.

* This instruction can also use the Index Register by adding i in front of the instruction.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See also Recall, Output


StoreS ss+,bb* [D>=12]

Pop X to semaphore ss, counting from base address bb. The base address bb is optional; if omitted it is taken to be 0. Any non-zero X value results in the semaphore being set to True.

For more details see the semaphore tutorial.

This instruction can be index by adding an 'i' in front of the instruction keyword. Indexing adds the contents of the index register I to the base address bb. That means it modifies the byte base address, not the bit address.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode. jndexing of semaphore addresses adds the contents of J to the bit address ss, not to the byte address bb. See Multi-channel MultiTrack: Semaphore addressing with J and I

The following is illegal: IasJ:iStoreS

Dialect restriction: Available only in boards with dialect 12 or greater.


XtoI

Copies X to I, Pops the register stack

See also ItoX


Input and Output instructions (digital)

Digital input and output instructions cover both the simple input reads and writes as well as more advanced instructions for testing and doing a conditional jump depending on the outcome. There are also instructions for Blinking an output (i.e. flashing light) and for generating auto-repeat keys.

In This Section

Blink oo+

BlinkMask mm

fPulse0 [D>=19]

GoIfInK ii+,LLLL

GoIfInOff ii+,LLLL

GoIfInOn ii+,LLLL

Input ii*+

InputF ii*+

InputFM ii+

InputK ii*+

InputO ii*+

InputOM ii+ [D>=13]

KBeep [D>=16]

KBeepOff

KBeepOn

Off oo*+

On oo*+

Output oo*+

OutputB oo+

OutputM oo+

ResetK

WaitOff ii+

WaitOffT ii+,tttt

WaitOn ii+

WaitOnK ii+

WaitOnKT ii+,tttt

WaitOnT ii+,tttt


Blink oo+

Blink output number oo. The selected output will start to turn on and off periodically. The default cadence is 400mS on, 400mS off. This can be changed with the BlinkMask instruction.

An output that is blinking will appear as on if tested with an InputO.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


BlinkMask mm

Uses the value mm to determine the cadence used for all outputs that receive or have received the Blink instruction.

mm is treated as a binary mask. Each bit represents a 200mS interval. For example, if mm=%10110010 the blink pattern will become:

200mS ON

200mS OFF

400mS ON

400mS OFF

200mS ON

200mS OFF

... repeat ...

Note that the one BlinkMask setting applies to all outputs that are blinking. That includes outputs that are blinking when a new BlinkMask is set. The default setting is %11001100

Dialect exclusions: Not available in dialects before 9.


InputFM ii+

Pushes the on/off state of 8 input points ii through ii+7 to X in individual bits of X. An On input gives a '1', and Off input gives a '0'.

In boards with dialects up to 11 the operand ii must be 0, 8, 16 ... etc (a multiple of 8) and must be less than or equal to the highest numbered input in the target SPLat board. Input points that don't exist return 0's in the corresponding bit positions in X. In dialect 12 and later the operand ii may be any number that exists as an input.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

This is a fast input instruction, i.e. there is no input debounce.

Dialect restriction. InputFM is implemented only in dialects 6 or later.

See also OutputM


InputOM ii+ [D>=13]

Pushes the on/off state of 8 output points ii through ii+7 to X in individual bits of X. An On output gives a '1', an Off output gives a '0'.

The operand ii may be any number that exists as an input. Input points that don't exist return 0's in the corresponding bit positions in X.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

This instruction will not readback an output ON condition resulting from an fPulse0 instruction.

Dialect restriction. InputOM is implemented only in dialects 13 or later.

See also OutputM, InputO


KBeep [D>=16]

This instruction generates a brief beep on the built in beeper, for example on an MMi201, providing the beep function is enabled by KBeepOn/KBeepOff. The beep generated is exactly the same as that generated when reading out a latched InputK input.

KBeep is not available on controllers that do not have a beeper.

Dialect exclusions: Not available in dialects before 16.

See also InputK, KBeepOn, KBeepOff


KBeepOff

Turn off the InputK beep function. It affects the automatically generated beeps plus the KBeep instruction.

On SPLat boards with onboard push buttons, default behaviour is that a short beep is generated when a latched button press is read out by an InputK or related instruction.

This instruction is implemented only on boards that have the appropriate hardware. On other boards it will generate a fatal runtime error.

Dialect exclusions: Not available in dialects before 11.

See also KBeepOn InputK WaitOnK GoIfInK


KBeepOn

Turn on the InputK beep function. It affects the automatically generated beeps plus the KBeep instruction.

On SPLat boards with onboard push buttons, default behaviour is that a short beep is generated when a latched button press is read out by an InputK or related instruction.

This instruction is implemented only on boards that have the appropriate hardware. On other boards it will generate a fatal runtime error.

Dialect exclusions: Not available in dialects before 11.

See also KBeepOff InputK WaitOnK GoIfInK


OutputB oo+

Outputs X to output number oo with blinking and pops the stack. If X is true the output will blink according to the prevailing BlinkMask. If X is false, the output will turn off.

An output that is blinking will appear as steady on if tested with an InputO.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.


OutputM oo+

Updates up to 8 outputs (from output oo through output oo+7) at once according to the bits patterns in X and Y.

In boards with dialects up to 11 the operand oo must be 0, 8, 16 ... etc (a multiple of 8) and must be less than or equal to the highest numbered output in the target SPLat board. Non-existent outputs are otherwise ignored. In dialect 12 and later the operand oo may be any number that exists as an output.

The bit pattern to be output is contained in Y. X contains an enable mask that defines which of the selected outputs is to be changed.. A 1-bit in X enables the output for update, a 0 disables it.

OutputM pops the stack twice (removing both X and Y)

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction. OutputM is implemented only in dialects 6 or later.

See also InputFM


ResetK

Clears the latched OFF to ON input transitions on all inputs at once.

Dialect exclusions: Not available in dialects before 9.


WaitOnK ii+

Normal SPLat inputs are capable of latching an OFF to ON transition. This is handy for such things as reading input button presses. The WaitOnK ii instruction will stall until a transition is registered on input ii. This could be a transition that was captured and stored before the instruction is executed. If there is no such prior transition, the instruction will wait indefinitely for a new transition. In a MultiTrack task this is not a problem.

Executing the WaitOnK instruction clears the latched transition, so a new OFF to ON transition would be required for a second WaitOnK to respond.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.

See also ResetK InputK WaitOn WaitOnT WaitOff WaitOffT WaitOnKT KBeepOn, KBeepOff GoIfInK


WaitOnKT ii+,tttt

This instruction is like the WaitOnK instruction, but with a built in time limit of tttt x 10mS. tttt is given in multiples of 10mS, up to a maximum of 32,767 x 10mS for dialects before 16, and 16,777,215 x 10mS for dialects 16 and later. When the instruction has executed, the next line of code should test the value in X to determine the outcome. X will be true if the expected event (an OFF to ON input transition on input ii) occurred. If the transition did not occur, X will be False.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller.

From dialect 16 this instruction becomes non-blocking when executed inside a MultiTrack task. When encountered in a MultiTrack task, it will always yield the processor the first time it is executed and continue yielding until the time has expired or the input condition is met.

Executing the WaitOnKT instruction clears any latched transition, so a new OFF to ON transition would be required for a second WaitOnKT to return a True value.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Warning: You cannot use Pause, WaitOnT, WaitOffT or WaitOnKT inside a MarkTime/LoopIfTiming loop.

Dialect exclusions: Not available in dialects before 9.

See also ResetK InputK WaitOn WaitOnT WaitOff WaitOffT WaitOnK Pause


Logical instructions

SPLat has logical operations that treat the whole data byte as a single boolean variable (anything nonzero is True) as well as instructions for bit-by-bit boolean operations. Instructions that operate on a bit by bit basis end in 'M' (which stands for "mask").

For example, suppose we have the following binary patterns in X and Y:

X %010101100

Y %001100101

The result of an OR will be %11111111 because both X and Y are non-zero, but the result of an OrM will be %011101101, i.e. will have a 1 wherever there is a 1 in either X or Y but a 0 where there is a 1 in neither.

From dialect 12 we also have semaphore instructions, which provide an easy way to perform single-bit manipulations directly on memory.

In This Section

And

AndM

BlinkMask mm

InputFM ii+

Not

NotM

Or

OrM

OutputM oo+

RolM

RorM

Xor

XorM


And

Forms the Boolean And of X and Y, Pops the register stack and places the And result in X.

Note: This instruction treats any non-zero byte value as True, and converts any non-zero byte result to all 1's.

See also Or, Xor, Not and the bit-wise Boolean instructions AndM, OrM, XorM, NotM


AndM

Performs a bit-wise Boolean AND between X and Y, popping the stack and leaving the result in X.

Dialect restriction. AndM is implemented only in dialects 6 or later.

See also OrM, XorM, NotM and the non-bit-wise equivalents And, Or, Xor, Not


Not

The Not instruction does a Boolean inversion on the contents of X:

True becomes False

False becomes True

Note: This instruction treats any non-zero byte value as True, and converts any non-zero byte result to all 1's.

See also And, Or, Xor and the bit-wise Boolean instructions AndM, OrM, XorM, NotM


NotM

Performs a bit-wise Boolean Not on X.

Dialect restriction. NotM is implemented only in dialects 6 or later.

See also AndM, OrM, XorM and the non-bit-wise equivalents And, Or, Xor, Not


Or

Forms the Boolean Or of X and Y, Pops the register stack and places the Or result in X.

Note: This instruction treats any non-zero byte value as True, and converts any non-zero byte result to all 1's.

See also And, Xor, Not and the bit-wise Boolean instructions AndM, OrM, XorM, NotM


OrM

Performs a bit-wise Boolean Or between X and Y, popping the stack and leaving the result in X.

Dialect restriction. OrM is implemented only in dialects 6 or later.

See also AndM, XorM, NotM and the non-bit-wise equivalents And, Or, Xor, Not


RolM

Rotates the contents of X left one bit position (direction 0->7), with bit 7 rotating to bit 0.

Dialect restriction. RolM is implemented only in dialects 6 or later.

See also RorM


RorM

Rotates the contents of X right one bit position (direction 7->0), with bit 0 rotating to bit 7.

Dialect restriction. RorM is implemented only in dialects 6 or later.

See also RolM


Xor

Forms the Boolean Exclusive-Or of X and Y, Pops the register stack and places the Xor result in X.

Note: This instruction treats any non-zero byte value as True, and converts any non-zero byte result to all 1's.

See also And, Or, Not and the bit-wise Boolean instructions AndM, OrM, XorM, NotM


XorM

Performs a bit-wise Boolean Exclusive-Or between X and Y, popping the stack and leaving the result in X.

Dialect restriction. XorM is implemented only in dialects 6 or later.

See also AndM, OrM, NotM and the non-bit-wise equivalents And, Or, Xor, Not


Memory instructions

Memory instructions include simple Store and Recall as well as counting, testing and floating point storage. From dialect 12 we also have semaphore instructions, which are listed here (because they reference memory) and under their own heading.

In This Section

BranchM mm+

iiChrPrintX dest# [D>=23]

ClrS ss+,bb* [D>=12]

ComRx_DelXChars [D>=23]

ComRx_GetHex [D>=23]

ComRx_ReadOne [D>=23]

DecM mm+

DecMGoIfNZ mm*+,LLLL

DecMRetIfNZ mm+

DecMRetIfZ mm+

DMGNZ mm*+,LLLL

iiChrPrintX dest# [D>=23]

iifPrintWFW dest#,f,p [D>=23]

iifPrintWVW dest#,f,p [D>=23]

iiHexPrintX dest# [D>=23]

iiPrintFill dest#,count,value [D>=23]

iiPrintNVText dest# [D>=27]

iiPrintText dest#,text-string [D >= 23]

iisPrintXFW dest# [D>=23]

iisPrintXVW dest# [D>=23]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]

fRecallQ mm*+

fRecallW mm*+

fStore mm*+

GoIfMEQ mm+,nn,LLLL

GoIfMGE mm+,nn,LLLL

GoIfMGT mm+,nn,LLLL

GoIfMLE mm+,nn,LLLL

GoIfMLT mm+,nn,LLLL

GoIfMNE mm+,nn,LLLL

GoIfMNZ mm+,LLLL

GoIfMZ mm+,LLLL

GoIfSF ss,bb+,LLLL [D>=12]

GoIfST ss,bb+,LLLL [D>=12]

GoSubIfMNZ mm+,LLLL

GoSubIfMZ mm+,LLLL

GoSubIfSF ss,bb+,LLLL [D>=12]

GoSubIfST ss,bb+,LLLL [D>=12]

iiHexPrintX dest# [D>=23]

IncM mm+

NotS ss+,bb* [D>=12]

PermRecall [D>=2]

PermStore [D>=2]

iiPrintFill dest#,count,value [D>=23]

Recall mm*+

RecallS ss+,bb* [D>=12]

RetIfMNZ mm+

RetIfMZ mm+

RetIfST ss,bb+ [D>=12]

RetIfSF ss,bb+ [D>=12]

SetMem mm*+,nn

SetS ss+,bb* [D>=12]

ShadowClear mm*+,cc

ShadowRead mm*+,cc

ShadowWrite mm*+,cc

iisPrintXFW dest# [D>=23]

iisPrintXVW dest# [D>=23]

Store mm*+

StoreS ss+,bb* [D>=12]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]


PermRecall [D>=2]

Restores the contents of data memories 6 through 18 from the Permanent memory. Data stored in this memory is retained when power is removed.

Returns a result code on the stack (in X) that depends on the outcome of the instruction:

Result codes are:

0 | Successful
1 | Old data was restored
2 | Fail. No data was restored.

Result code 1 means the most recently saved data could not be retrieved and an older version was retrieved instead. See the Classic tutorial in the SPLat/PC help file for more details.

Dialect exclusions: Not available in dialects before 2

See also PermStore ShadowClear ShadowRead ShadowWrite


PermStore [D>=2]

Saves the contents of data memories 6 through 18 in the non-volatile memory. Data stored in this memory is retained when power is removed.

This instruction has no effect on the data stack.

Note: PermStore takes a significant amount of time to perform (10's to 100's of milliseconds, depending on which SPLat board you are using), because of the time it takes to write data to the non-volatile memory chip. While this is taking place no other instructions are executed. Be careful therefore to use PermStore only when it is safe for your program to "go to sleep" for a time. The write time is detailed under the documentation for each SPLat product.

Dialect exclusions: Not available in dialects before 2

See also PermRecall ShadowClear ShadowRead ShadowWrite


RetIfST ss,bb+ [D>=12]

Return from subroutine if semaphore ss is true. The semaphore base address bb is optional; if omitted it is taken to be 0.

For more details see the semaphore tutorial.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: Available only in boards with dialect 12 or greater.


RetIfSF ss,bb+ [D>=12]

Return from subroutine if semaphore ss is false. The semaphore base address bb is optional; if omitted it is taken to be 0.

For more details see the semaphore tutorial.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect restriction: Available only in boards with dialect 12 or greater.


Multi-tasking support (Deprecated)

Note: This method of multitasking, introduced in dialect 12, is rendered largely obsolete by the MultiTrack system introduced in dialect 16.

In This Section

Suspend mm*+ [D>=12/15]

Resume mm*+ [D>=12/15]


Resume mm*+ [D>=12/15]

Retrieve the memory address stored in RAM at mm and mm+1, and jump to that address.

See also Suspend

Note: If the address retrieved contains 0, indicating it has not been initialized, the instruction will "fall through" so the following instruction is executed.

This is a special instruction used specifically for implementing multitasking programs. Multitasking tutorial

Dialect restriction: Available only in boards with dialect 12 or greater.

Indexing: From dialect 15 this instruction can be indexed by adding 'i' in front of it. Because suspend/resume addresses occupy 2 bytes of RAM, the index register is multiplied by 2 before being applied to mm.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Note: This method of multitasking, introduced in dialect 12, is rendered largely obsolete by the MultiTrack system introduced in dialect 16.


MultiTrack instructions

MultiTrack is an extremely easy to use multitasking system that was introduced into SPLat in dialect 16 (September 2005). Tutorial

The instructions listed in this section are those that are primarily or only associated with MultiTrack. Virtually every instruction in SPLat may however be used in MultiTrack.

In This Section

BranchJ [D>=16]

ClrInstCount [D>=18]

fSTTimeSince aa+ [D>=16]

GetTick100 [D>=12]

KillTask [D>=16]

LaunchTask LLLL,jj [D>=16]

LaunchTaskJ LLLL [D>=16]

LaunchTaskX LLLL [D>=16]

LoopIfTiming tttttt,LLLL [D>=16]

MarkTime [D>=16]

RunTasks [D>=16]

RunTasksForever [D>=16]

STScale ss [D>=16]

STStart aa+ [D>=16]

STTest aa+,tttttt [D>=16]

WaitForRTCEvent e+ [D>=16]

WaitForSF ss,bb [D>=16]

WaitForST ss,bb [D>=16]

YieldTask [D>=16]


ClrInstCount [D>=18]

If a MultiTrack task executes more than 255 instructions without yielding, the controller will suffer a fatal runtime error. This is a deliberate mechanism designed to prevent you from locking up your program with an infinite loop in a task. However, there may be times when you need to exceed this limit. The ClrInstCount instruction resets the counter each time it is executed, effectively defeating the protection mechanism.

A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 18.


GetTick100 [D>=12]

Reads and decrements the global 100mS time tick counter. The counter value is pushed into X.

The 100mS time tick counter is an 8-bit counter that counts up every 100mS but will not count past 255. Each time it is read out with this instruction it is decremented by 1 but not past 0. This allows you to devise a variety of timing schemes. If your program is busy with something else for say 10 seconds, when it gets back to testing for ticks there will be 10 seconds' worth of ticks (about 100) accumulated, so the next 100 GetTick100 instructions will return true. This allows you to create timing schemes that can "make up for lost time".

In SPLat/PC the simulator will simulate one 100mS tick for every 2000 instructions simulated. This is a rough approximation to an average instruction execution time of 50uS. It is impossible to accurately simulate real-time.

This instruction is deprecated. Do not use in new programs.

See also GetTick10 STScale

Dialect exclusions: Not available in dialects before 12.


KillTask [D>=16]

Removes the current task from the MultiTrack task queue and yields the processor to the next task. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


LaunchTask LLLL,jj [D>=16]

Launches the MultiTrack task that starts at line LLLL, The task address is entered into the task queue for later execution when the task queue is run using RunTasksForever or RunTasks.

jj is an optional argument that presets the J register for the task. See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


LaunchTaskJ LLLL [D>=16]

Launches the MultiTrack child task that starts at line LLLL. The J-register value is inherited from the parent task The task address is entered into the task queue for later execution when the task queue is run using RunTasksForever or RunTasks.

This instruction can only be executed from within a task running under MultiTrack. See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


LaunchTaskX LLLL [D>=16]

Launches the MultiTrack task that starts at line LLLL with the task's J register set to the value in X and pops the register stack. The task address is entered into the task queue for later execution when the task queue is run using RunTasksForever or RunTasks.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


LoopIfTiming tttttt,LLLL [D>=16]

Compares the current system time with the value captured by a previous MarkTime instruction in the same MultiTrack task. If the elapsed time is less than tttttt times 10mS, jumps to line LLLL, otherwise "falls through" to the next instruction. tttttt is a value between 0 and 16,777,215. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller.

Caution: The MarkTime/LoopIfTiming mechanism uses the very same timer as Pause, WaitOnT etc. Do not use MarkTime/LoopIfTiming to bracket a section of code containing Pause, WaitOnT etc.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


MarkTime [D>=16]

Captures the current System Timer value to the the current task's timer register. This can subsequently be used for timing calculations. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue. This is a companion to LoopIfTiming

Warning: You cannot use Pause, WaitOnT, WaitOffT or WaitOnKT inside a MarkTime/LoopIfTiming loop

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


RunTasks [D>=16]

Cause one run through of all the tasks that have previously been entered into the MultiTrack task queue by LaunchTask, LaunchTaskX or LaunchTaskJ instructions. If any other tasks are launched by those tasks, they may or may not be run before RunTasks completes.

This instruction is deprecated. Do not use in new programs.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


RunTasksForever [D>=16]

Causes continuous and repeated running of all the tasks that have previously been entered into the MultiTrack task queue by LaunchTask, LaunchTaskX or LaunchTaskJ instructions and any tasks launched by those tasks. RunTasksForever never terminates.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


STScale ss [D>=16]

This instruction is intended to assist debugging using the SuperTimer mechanism plus the global 100mS and 10mS ticks. ss is a speed-up factor between 1 and 255. 1 gives no speedup. 255 gives a 255x speedup. This instruction is not confined to use in a MultiTrack task.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller. This is a different mechanism to STScale. With STScale the ticks still happen every 10mS, but more than 10mS is added to the system clock. That means that with STScale the system clock stil only changes every 10mS. STScale is simulated in SPLat/PC, whereas the CPU device setting is not. STScale is intended only for speedup during debugging.

Note that a zero argument will stop the supertimers and anything derived from them (viz Pause and WaitOnT timing in a MultiTrack task). Be careful.

SuperTimers are discussed in the MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.

Will not be supported in 32-bit boards. Instead the CPU command SPxCmd1 1,!CPU lets you change timing of individual MultiTrack tasks.

See also STStart, STTest, Pause, WaitOnT, WaitOnKT


STStart aa+ [D>=16]

(SuperTimer Start) Captures the current value of the system timer and stores it in 3 bytes of RAM at address aa. (Normally aa would be declared in a defTIME24 directive). This instruction is not confined to use in a MultiTrack task.

SuperTimers are discussed in the MultiTrack tutorial

The address argument aa will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 16.

See also STScale, STTest, Pause, WaitOnT, WaitOnKT


STTest aa+,tttttt [D>=16]

Compares the current system time with the value stored in 3 bytes of RAM at address aa. If the difference (elapsed time since the System Time was captured to aa) is less than tttttt x 10mS, returns True in X. Otherwise returns False in X. This instruction is not confined to use in a MultiTrack task. It is based on the assumption that there was a prior STStart instruction. tttttt is a value between 0 and 16,777,215.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller, look for SPxCmd1 1,!CPU

The address argument aa will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

SuperTimers are discussed in the MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.

See also STStart, STScale, Pause, WaitOnT, WaitOnKT


WaitForRTCEvent e+ [D>=16]

Wait for RTC event e to trip. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

The event number e will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

See MultiTrack tutorial and RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


WaitForSF ss,bb [D>=16]

When first executed causes a MultiTrack task to yield. Thereafter, causes the task to yield if the semaphore ss, counting from base address bb is True (hence the next instruction will only get executed once the semaphore is False). The base address bb is optional; if omitted it is taken to be 0. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

Note: SPLat/PC allows a single symbolic address to be used for a semaphore that is declared in defSEM directives. In this case the syntax becomes

WaitForSF   sName

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


WaitForST ss,bb [D>=16]

When first executed causes a MultiTrack task to yield. Thereafter, causes the task to yield if the semaphore ss, counting from base address bb is False (hence the next instruction will only get executed once the semaphore is True). The base address bb is optional; if omitted it is taken to be 0. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

Note: SPLat/PC allows a single symbolic address to be used for a semaphore that is declared in defSEM directives. In this case the syntax becomes

WaitForST   sName

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


YieldTask [D>=16]

Places the current task back into the MultiTrack task queue and yields control of the processor. This allows the next task in turn to run. A fatal error will occur if this instruction is executed outside of a task in the MultiTrack task queue.

See MultiTrack tutorial

Dialect exclusions: Not available in dialects before 16.


Non-volatile memory

There are three different schemes for non-volatile data storage in SPLat, meaning the data is preserved even if power goes off. Most schemes are based on FLASH or EEPROM memory technology, though in future we may introduce memory types using battery backup or other technologies. FLASH technology has a wear-out effect, meaning it can only endure a certain number of writes before it ceases working. The various schemes give you a trade-off between size and endurance.

The oldest scheme is called Permanent memory. It consists of 13 bytes, and has a typical endurance of 500,000 writes.

A more recent scheme is called Shadow memory. It allows all of RAM to be backed up (shadowed) into non-volatile memory, but with a lower endurance than Permanent memory. The actual endurance will vary from product to product.

The most recent scheme is Non-Volatile Extended Memory (NVEM). This is a universal model for large non-volatile memories, and was introduced in April 05.

See also MMi99/200 Non-volatile memory SL99 Non-volatile memory NVEM documentation

In This Section

NV0Byte Directive [D>=15]

NV0fNum Directive [D>=15]

NV0Ptr Directive [D>=15]

NV0Space Directive [D>=15]

NVAddPtr nn [D>=15]

NVAdvPtr [D>=15]

NVDecRecNum [D>=15]

NVEM0 Directive [D>=15]

NVfGetEndW [D>=15]

NVfGetPtrW [D>=15]

NVfGetRecNumW [D>=15]

NVfPutPtrW [D>=15]

NVfPutRecNumW [D>=15]

NVfReadQ nn*+ [D>=15]

NVfReadW nn*+ [D>=15]

NVfWriteW nn*+ [D>=15]

NVIncRecNum [D>=15]

NVPopByte nn*+ [D>=15]

NVPopPage [D>=15]

NVPopRecLen [D>=15]

NVPopRecNum [D>=15]

NVPushByte nn*+ [D>=15]

NVPushPage [D>=15]

NVPushRecLen [D>=15]

NVPushRecNum [D>=15]

NVReadRec mm [D>=15]

NVReadToPtr nn [D>=15]

NVSetPage nn [D>=15]

NVSetPtr pppp [D>=15]

NVSetRecLen nn [D>=15]

NVSetRecNum nnnn [D>=15]

NVSubPtr nn [D>=15]

NVtoUV nn,u [D>=15]

NVWriteRec mm [D>=15]

OBLCD_NVText nn [D>=15]

PermRecall [D>=2]

PermStore [D>=2]

ShadowClear mm*+,cc

ShadowRead mm*+,cc

ShadowWrite mm*+,cc


NV0Byte Directive [D>=15]

This applies only to SPLat/PC versions 6.15.1 or later, and is relevant only to SPLat controllers with language dialect 15 or later.

The NV0Byte directive causes SPLat/PC to create one or more data bytes in NVEM0 memory. The argument to NV0Byte consists of a comma-delimited list of bytes in any of the following formats

Example | Max value | Description
23 | 255 | Decimal constant
'A3 | 255 | Hex constant
%10101100 | 255 | Binary constant
FooBar | 255 | A previously EQUated constant
T | 255 | Boolean True = %11111111
F | 255 | Boolean False = %0000000
"Hello" |  | A quoted text string
#WORD | 65535 | A 15 or 16 bit value preceded by #. New in SPLat/PC V7.16.4

Strings may be any printable characters, enclosed in quotes. A quotation mark is represented by two quotation marks in a row, viz ""

Any number of individual bytes, words and strings may be concatenated, with commas separating them. Spaces are not allowed except within quoted strings.

The #WORD form is supported in SPLat/PC V7.16.4 and later. It simplifies building of ModBus master scripts. It allows 16-bit values to be embedded in byte lists, but does have the limitation that values over 32767 expressed in anything other than decimal will be rejected. It is also not possible to include a 16-bit NVEM pointer value.

Examples:

WordConst  EQU	      51234                     ;A long decimal constant
MaxSpeed   EQU       100                       ;A byte constant
MyTable:   NV0Byte   45                        ;Decimal constant
           NV0Byte   'A9                       ;Hex constant
           NV0Byte   %10101100                 ;Binary constant
           NV0Byte   45,'A9,%10101100          ;All 3 of above combined
           NV0Byte   MaxSpeed                  ;Symbolic constant defined in an EQU
           NV0Byte   "The quick brown fox"     ;Simple text string
           NV0Byte   "Said ""I will bite you""" ;Quotes within a string
           NV0Byte   "Hello World",'0D,'0A     ;Mixed
           NV0Byte   MaxSpeed,#WordConst,"OK"  ;Mixed

Please refer to the NVEM0 documentation for more details and examples.


NV0fNum Directive [D>=15]

This applies only to SPLat/PC versions 6.15.0 or later, and is relevant only to SPLat controllers with language dialect 15 or later.

The NV0fNum directive causes SPLat/PC to create a 4-byte floating point number in NVEM0 memory.

Example:

MyTable:   NV0fNum   3.1415926
           NV0fNum   +Inf

This will cause the floating point values 3.1415926 and Infinity to be stored in NVEM0 locations MyTable and MyTable+4 (occupying 4 bytes per value).

Notice that you can use the irrational number +Inf and -Inf (Infinity and -Infinity are also allowed). These generate the corresponding floating point representations. This may be useful for creating end of table sentinels (markers). You can also use NaN (Not a Number). Whether either of these makes sense depends on how you use the result codes returned by many floating point operations.

Please refer to the NVEM0 documentation for more details on the use of NVEM0.


NV0Ptr Directive [D>=15]

This applies only to SPLat/PC versions 6.15.0 or later, and is relevant only to SPLat controllers with language dialect 15 or later.

The NV0Ptr directive causes SPLat/PC to create a 2-byte pointer in NVEM0 memory that points to another NVEM location. It is used for creating tables of pointers. This may be useful where for example you have a table of different length strings, and therefore need a table to point into it.

Example:

TextTable:
Text0      NVByte     16,"I can't do that,"
Text1      NVByte     6," Dave."
Text2      NVByte     23,"I'm not really wicked, "
Text3      NVByte     27,"I was simply drawn that way"

PointerTable:

           NV0Ptr     Text0

           NV0Ptr     Text1

           NV0Ptr     Text2

           NV0Ptr     Text3

Please refer to the NVEM0 documentation for more details. In particular see the NVReadToPtr instruction.


NV0Space Directive [D>=15]

This applies only to SPLat/PC versions 6.15.0 or later, and is relevant only to SPLat controllers with language dialect 15 or later.

The NV0Space directive causes SPLat/PC to reserve a certain number of bytes in NVEM0. It is useful for defining blocks of NVEM0 memory for later writing to, and being able to assign label names to them.

Syntax:

             NV0Space    ssss

ssss can be any value from 0 to 32,767 (within the memory capacity of the target board!)

Example:

DataArea1:   NV0Space    12
DataArea2:   NV0Space    126
DataArea3:   NV0Space    98

Please refer to the NVEM0 documentation for more details.


NVAddPtr nn [D>=15]

Add nn (8 bits) to the NVEM pointer. Raise a fatal runtime error if the result overflows the register.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVAdvPtr [D>=15]

Advance the NVEM pointer by RecLen bytes. Raise a fatal runtime error if the result overflows the register.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVDecRecNum [D>=15]

Subtract 1 from RecNum. Raise a fatal error if the value goes negative.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVEM0 Directive [D>=15]

This applies only to SPLat/PC versions 6.15.0 or later, and is relevant only to SPLat controllers with language dialect 15 or later.

The NVEM0 directive is used to mark the boundary between executable code and NVEM0 memory space. SPLat/PC uses it to recognise where to start looking for and processing data for pre-loading into the NVEM0 memory.

If you are using NVEM type 0, this directive must appear before any NV0xxxx directives like NV0Byte, and there must be no executable instructions after the NVEM0 directive. You may only have one NVEM0 directive in a program.

Please refer to the NVEM0 documentation for more details.


NVIncRecNum [D>=15]

Add 1 to RecNum. Raise a fatal error if the value exceeds the capacity of the RecNum register.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVPopPage [D>=15]

Pop X to the 8-bit NVEM page select register. Raise a fatal runtime error if the page does not exist.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVPopRecLen [D>=15]

Pop X to the NVEM RecLen (record length) register.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVPopRecNum [D>=15]

Pop X to the RecNum register. This can only be used for accessing records 0-255. No error detection.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVPushPage [D>=15]

Push the 8-bit NVEM page select register to X.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVPushRecLen [D>=15]

Push the NVEM RecLen (record length) register to X.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVPushRecNum [D>=15]

Push the record number to X. Raise a fatal error if the value exceeds 255.

Record numbers over 255 are perfectly legal (depending on the type of NVEM), but cannot be accommodated in X. Hence, you can handle records up to 255 using the register stack, but you would have to use floating point registers to manipulate higher numbered records.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVReadRec mm [D>=15]

Read the record at Pointer + RecLen x RecNum to RAM at mm, transferring RecLen bytes. Raise a fatal error if the transfer runs past the end of NVEM or past end of RAM.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVReadToPtr nn [D>=15]

Read a pointer value from an NVEM table to the NVEM pointer. Raise a fatal runtime error if the read goes past the end of the NVEM page.

The data is read from the NVEM address Pointer + RecLen x RecNum + nn. 2 bytes of data are transferred.

This instruction replaces the existing pointer value with a new one from the table, and is the key to having tables of pointers.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVSetPage nn [D>=15]

Set the 8-bit NVEM page select register to nn. Raise a fatal runtime error if the page does not exist.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVSetPtr pppp [D>=15]

Set the NVEM pointer to the 16-bit value pppp.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVSetRecLen nn [D>=15]

Set RecLen to the number nn.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVSetRecNum nnnn [D>=15]

Set the RecNum register to the number nnnn. Raise a fatal error if the value exceeds the capacity of the RecNum register.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVSubPtr nn [D>=15]

Subtract nn (8 bits) from the NVEM pointer. Raise a fatal runtime error if the result is negative.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVtoUV nn,u [D>=15]

Copy RecLen to V. Then copy RecLen bytes from NVEM at Pointer + RecLen x RecNum + nn to U starting at U(u). Raise a fatal error if it runs off the end of NVEM or U.

This instruction is the basis of tables of text strings for the LCD. See also OBLCD_NVText

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.


NVWriteRec mm [D>=15]

Write a record from RAM at mm to NVEM at Pointer + RecLen x RecNum, transferring RecLen bytes. Raise a fatal error if the transfer runs past the end of NVEM or past end of RAM.

Refer to NVEM documentation to learn about Non Volatile Extended Memory.

Dialect exclusions: Not available in dialects before 15.

Note: NVEM has significant limits on how many write cycles it can withstand. Please refer to the documentation for your particular controller to determine what the limitations are. Too many writes are likely to to permanently damage the controller and void the warranty.


Number comparison and testing instructions

There are a large number of instructions for testing numbers and acting on the result. These include floating point comparisons and simple count down and test instructions for building program loops.

In This Section

Branch

BranchM mm+

BranchR

Compare

CompareR

DecMGoIfNZ mm*+,LLLL

DecMRetIfNZ mm+

DecMRetIfZ mm+

DMGNZ mm*+,LLLL

fCompareR

fGoIfNeg LLLL

fGoIfNZ LLLL

fGoIfPos LLLL

fGoIfWGEQ LLLL [D>=23]

fGoIfWGTQ LLLL [D>=23]

fGoIfWLEQ LLLL [D>=23]

fGoIfWLTQ LLLL [D>=23]

fGoIfZ LLLL

fTest

fTestWeqQ [D>=24]

fTestWeqZ [D>=25]

fTestWgeQ [D>=25]

fTestWgeZ [D>=25]

fTestWgtQ [D>=25]

fTestWleQ [D>=25]

fTestWltQ [D>=25]

fTestWltZ [D>=25]

fTestWneQ [D>=25]

fTestWneZ [D>=25]

GoIfINZ LLLL

GoIfIZ LLLL

GoIfMEQ mm+,nn,LLLL

GoIfMGE mm+,nn,LLLL

GoIfMGT mm+,nn,LLLL

GoIfMLE mm+,nn,LLLL

GoIfMLT mm+,nn,LLLL

GoIfMNE mm+,nn,LLLL

GoIfMNZ mm+,LLLL

GoIfMZ mm+,LLLL

GoIfNZ LLLL

GoIfXEQ nn,LLLL

GoIfXGE nn,LLLL

GoIfXGT nn,LLLL

GoIfXLE nn,LLLL

GoIfXLT nn,LLLL

GoIfXNE nn,LLLL

GoIfZ LLLL

GoSubIfMNZ mm+,LLLL

GoSubIfMZ mm+,LLLL

GoSubIfNZ LLLL

GoSubIfXEQ nn,LLLL

GoSubIfXGE nn,LLLL

GoSubIfXGT nn,LLLL

GoSubIfXLE nn,LLLL

GoSubIfXLT nn,LLLL

GoSubIfXNE nn,LLLL

GoSubIfZ LLLL

TestXeqY [D>=28]

TestXgeY [D>=28]

TestXgtY [D>=28]

TestXleY [D>=28]

TestXltY [D>=28]

TestXneY [D>=28]

Result codes for CompareR

Result codes for fCompareR


Compare

Compare makes a numeric comparison between X and Y, and leaves a result code in X.

The result codes are:

X vs Y | Code
X=Y | 0
X>Y | 1
X<Y | 2

The original X and Y values are Popped off the register stack.

Dialect restriction: Compare is implemented only in dialects 4 or later.

See also CompareR, fCompareR


CompareR

CompareR makes a numeric comparison between X and Y, and leaves a result code in R.

The original X and Y values are not affected.

This instruction is functionally very close to the older Compare instruction, but takes advantage of the new R-register introduced in dialect 6. Also, it does not pop the stack

Dialect restriction: CompareR is implemented only in dialects 6 or later.

See also Compare BranchR


Result codes for CompareR

X vs Y | R
X=Y | 0
X>Y | 1
X<Y | 2

These results can be tested using BranchR


Onboard LCD (OBLCD) instructions

The On Board Liquid Crystal Display (OBLCD) is supported with a comprehensive set of instructions for text and numeric display. The OBLCD is fully documented in the OBLCD guide.

The LCD instructions are actually composite instructions that get compiled by SPLat/PC. When SPLat/PC sees one of these instructions it translates it into several low level instructions, including ones that communicate with the LCD via the UV registers (see SPLat expansion framework). You don't have to worry about the details other than to be aware that these instructions will consequently modify UV.

In This Section

OBLCD_CharX [D>=

OBLCD_Cls

OBLCD_CurBlink

OBLCD_CurOff

OBLCD_CurOn

OBLCD_Date [D>=16]

OBLCD_DM [D>=16]

OBLCD_Dim Lo,Hi,Delay

OBLCD_fDispW f,p

OBLCD_GetCurYX [D>=16]

OBLCD_GoIfBusy line

OBLCD_Dim Lo,Hi,Delay

OBLCD_GoIfIdle line

OBLCD_HexDispX

OBLCD_HGraph [D>=18]

OBLCD_HHMM [D>=16]

OBLCD_HHMMSS [D>=16]

OBLCD_NVText nn [D>=15]

OBLCD_RUT

OBLCD_SDecDispMFW mm

OBLCD_SDecDispMVW mm

OBLCD_SDecDispXFW

OBLCD_SDecDispXVW

OBLCD_SetCur r,c

OBLCD_SetCurYX [D>=16]

OBLCD_SpclChar c

OBLCD_Text "Message"

OBLCD_Type tt

OBLCD_UDecDispMFW mm

OBLCD_UDecDispMVW mm

OBLCD_UDecDispXFW

OBLCD_UDecDispXVW


OBLCD_Date [D>=16]

Display the HH, MM and SS registers (assumed to contain a date) on the LCD in the format yy/mm/dd, e.g. 05/09/26 for September 26 2005.

Note: This instruction alters UV.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


OBLCD_DM [D>=16]

Display the DM/DN register on the LCD. The display produced consists of 7 characters, one for each day of the week. The day number corresponding to the number in DN is said to be the selected day. A day for which the corresponding bit in DM is set is said to be on, otherwise it is off. If a day is ON, it is represented by the first letter of its name. If it is OFF it is represented by a hyphen or an underscore. The selected day is represented by its upper case first letter if ON or by its lower case first letter if OFF. The following table shows all the possibilities:

 | ON | OFF
Selected | Upper case 1st letter | Underscore
Not selected | Lower case first letter | Hyphen

Note: This instruction alters UV.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


OBLCD_Dim Lo,Hi,Delay

Sets the parameters for display dimming. Lo is the low (dimmed) backlight setting. Hi is the high (bright) backlight setting. Delay is the delay in minutes before the display dims.

The white LED technology used for the blue backlight has a lifetime of approximately 3000 hours. The backlight should therefore be dimmed when not in use. This instruction provides automatic dimming after a set delay. The backlight is automatically brought back to Hi brightness when an Enduser presses a front panel button, and dimmed down to Lo brightness after Delay minutes.

Works only on controllers that are fitted with a dimmable display. This includes all MS120 versions, plus MMi202 with a dimmable display and firmware version V3.21 or later.

Allowable number ranges are 0-8 for Lo and Hi, and 0-255 for Delay.

This instruction should only be used after OBLCD_Type (which is only needed once at the start of a program (and not at all with 2x16 displays like on the MS120).

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9.


OBLCD_HHMM [D>=16]

Display the HH and MM registers on the LCD in the format hh:mm, e.g. 21:09.

Note: This instruction alters UV.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


OBLCD_HHMMSS [D>=16]

Display the HH, MM and SS registers on the LCD in the format hh:mm:ss, e.g. 21:09:43.

Note: This instruction alters UV.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


Onboard counter A (OBCA) Instructions

In This Section

OBCA_Clr

OBCA_fRead

OBCA_fRdClr

OBCA_GoIfNoRdg line

OBCA_GoIfRdg line

OBCA_GoSubIfRdg line

OBCA_Mode m

OBCA_RetIfNoRdg

OBCA_WaitRdg


Onboard quadrature counter

In This Section

OBQC_Clr c [D>=16]

OBQC_fGet c [D>=16]

OBQC_fSet c [D>=16]

OBQC_Status c [D>=17]


RealTime Clock

The RealTime Clock (RTC) provides date and calendar functions and the ability to make program actions take place at certain times. It is documented here.

In This Section

GoIfRTCEvent e+,LLLL [D>=16]

OBLCD_Date [D>=16]

OBLCD_DM [D>=16]

OBLCD_HHMM [D>=16]

OBLCD_HHMMSS [D>=16]

RTCDecDN [D>=25]

RTCDecHH [D>=25]

RTCDecMM [D>=25]

RTCDecSS [D>=25]

RTCClrDM [D>=16]

RTCDateToUV [D>=16]

RTCDMToUV [D>=16]

RTCFailFlag [D>=20]

RTCHHMMSSToUV [D>=16]

RTCHHMMToUV [D>=16]

RTCIncDN [D>=16]

RTCIncHH [D>=16]

RTCIncMM [D>=16]

RTCIncSS [D>=16]

RTCNotDM [D>=16]

RTCPopDM [D>=16]

RTCPopDN [D>=16]

RTCPopHH [D>=16]

RTCPopMM [D>=16]

RTCPopSS [D>=16]

RTCPushDM [D>=16]

RTCPushDN [D>=16]

RTCPushHH [D>=16]

RTCPushMM [D>=16]

RTCPushSS [D>=16]

RTCReadDate [D>=16]

RTCReadEvent e+ [D>=16]

RTCReadTime [D>=16]

RTCSetDM [D>=16]

RTCTestEvent e+ [D>=16]

RTCWriteDate [D>=16]

RTCWriteDN [D>=16]

RTCWriteEvent e+ [D>=16]

RTCWriteTime [D>=16]

WaitForRTCEvent e+ [D>=16]


RTCDecDN [D>=25]

Decrements the DN register.

The behaviour is that it decrements to 0 then rolls under to 6. Day 0 corresponds to Monday.

Some 32-bit boards, such as EC1, automatically calculate the day number from the date. In such boards this instruction will be ignored.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCDecHH [D>=25]

Decrements the HH register.

The behaviour depends on whether the last RTCRead was a time/event read or the date. If it was a time or event, the register decrements up to 0 then rolls under to 23. If the last read was the date, it decrements to 0 then rolls under to 99.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCDecMM [D>=25]

Decrements the MM register.

The behaviour depends on whether the last RTCRead was a time/event read or the date. If it was a time or event, the register decrements up to 0 then rolls under to 59. If the last read was the date, it decrements to 1 then rolls under to 12.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCDecSS [D>=25]

Decrements the SS register.

The behaviour depends on whether the last RTCRead was a time/event read or the date. If it was a time or event, the register is unchanged (seconds cannot be altered). If the last read was the date, it decrements to 1, then rolls under to the last valid day of the current month and (leap) year.

It is possible to create an invalid month/day combination by setting the day of month to 31 then setting the month to say 2. This will be corrected to a valid day of month when an RTCWriteDate is executed.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCClrDM [D>=16]

Clears the bit in the DM register that is selected by DN. Thus if DN=3 (Thursday) bit 3 of DM will be cleared to 0.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCDateToUV [D>=16]

Formats HH, MM and SS (assumed to contain a valid date) to U0-U7 as an ASCII string in the format 05/06/29 (yy/mm/dd) and sets V to 8.

This is a "low level" UV instruction which you are unlikely to need. It is used by the LCD display instructions.

Dialect exclusions: Not available in dialects before 16.

See also OBLCD_Date


RTCDMToUV [D>=16]

Copies DM to U0 and DN to U1.

This is a "low level" UV instruction which you are unlikely to need. It is used by the LCD display instructions.

Dialect exclusions: Not available in dialects before 16.

See also OBLCD_DM


RTCFailFlag [D>=20]

Push the RTC failure flag into X. This will be true if the RTC has not been initialised, which indicated that it's backup power has expired.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 20.


RTCHHMMSSToUV [D>=16]

Formats HH, MM and SS to U0-U7 in the ASCII format 12:34:01 and sets V to 8.

This is a "low level" UV instruction which you are unlikely to need. It is used by the LCD display instructions.

Dialect exclusions: Not available in dialects before 16.

See also: OBLCD_HHMMSS


RTCHHMMToUV [D>=16]

Formats HH and MM to U0-U4 in the ASCII format 12:34 and sets V to 5.

This is a "low level" UV instruction which you are unlikely to need. It is used by the LCD display instructions.

Dialect exclusions: Not available in dialects before 16.

See also: OBLCD_HHMM


RTCIncDN [D>=16]

Increments the DN register.

The behaviour is that it increments to 6 then rolls over to 0. Day 0 corresponds to Monday.

Some 32-bit boards, such as EC1, automatically calculate the day number from the date. In such boards this instruction will be ignored.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCIncHH [D>=16]

Increments the HH register.

The behaviour depends on whether the last RTCRead was a time/event read or the date. If it was a time or event, the register increments up to 23 then rolls over to 0. If the last read was the date, it increments to 99 then rolls over to 0.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCIncMM [D>=16]

Increments the MM register.

The behaviour depends on whether the last RTCRead was a time/event read or the date. If it was a time or event, the register increments up to 59 then rolls over to 0. If the last read was the date, it increments to 12 then rolls over to 1.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCIncSS [D>=16]

Increments the SS register.

The behaviour depends on whether the last RTCRead was a time/event read or the date. If it was a time or event, the register is unchanged (seconds cannot be altered). If the last read was the date, it increments to the last valid day of the current month and (leap) year, then rolls over to 1.

It is possible to create an invalid month/day combination by setting the day of month to 31 then setting the month to say 2. This will be corrected to a valid day of month when an RTCWriteDate is executed.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCNotDM [D>=16]

Inverts the bit in the DM register that is selected by DN. Thus if DN=3 (Thursday) bit 3 of DM will be inverted.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPopDM [D>=16]

Pop X to the DM register. Bit 7 is forced to 0.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPopDN [D>=16]

Pop X to the DN register. If the number is >6, it is forced to 0.

Some 32-bit boards, such as EC1, automatically calculate the day number from the date. In such boards this instruction will be ignored.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPopHH [D>=16]

Pop X to the HH register. This could represent hours or year.

It is possible to create an invalid day/ month/ time if you provide incorrect data, though the RTCWriteEvent/Date commands force these to valid values.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPopMM [D>=16]

Pop X to the MM register

It is possible to create an invalid day/ month/ time if you provide incorrect data, though the RTCWriteEvent/Date commands force these to valid values.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPopSS [D>=16]

Pop X to the SS register. This could represent seconds or day of month.

It is possible to create an invalid day/ month/ time if you incorrect data, though the RTCWriteEvent/Date commands force these to valid values.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPushDM [D>=16]

Push the DM register into X.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPushDN [D>=16]

Push the DN register into X.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPushHH [D>=16]

Push the HH register into X. This could represent hours or year.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPushMM [D>=16]

Push the MM register into X.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCPushSS [D>=16]

Push the SS register into X. This could represent seconds or day of month.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCReadDate [D>=16]

Reads the RTC date to HH (year), MM (month), SS (day of month) and DN (Day Number). Also sets all bits of the DM register.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCReadEvent e+ [D>=16]

Reads RTC event number e to HH (hours), MM (minutes) and DM (day mask). Also sets the DN register to the current day number.

The event number e will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCReadTime [D>=16]

Reads the RTC time to HH (hours), MM (minutes), SS (seconds) and DN (Day Number). Also sets the bit in the DM register corresponding to the day number.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCSetDM [D>=16]

Sets the bit in the DM register that is selected by DN. Thus if DN=3 (Thursday) bit 3 of DM will be set.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCTestEvent e+ [D>=16]

Tests if event e is tripped and pushes the result into X. X becomes True if the event is tripped.

An event can only be tested for tripped once per trip. See RTC documentation and tutorial

The event number e will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 16.


RTCWriteDate [D>=16]

Writes the date from HH (year), MM (month) and SS (day of month) to the RTC. The day of month will be forced to a number that is valid for the month and year.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCWriteDN [D>=16]

Writes the current day number from the DN register to the RTC.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCWriteEvent e+ [D>=16]

Writes the time from HH (hours), MM (minutes) and DM (day mask) to RTC event e. The events are non-volatile, i.e. they are preserved when the power goes off. For more information see the RTC documentation for your specific controller.

The event number e will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

The memory used to store RTC events may have a finite write cycle endurance. That means executing too many RTCWriteEvent instructions may damage it. This is documented under individual products.

Please refer to the product documentation for your particular controller to find out how many events it supports.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


RTCWriteTime [D>=16]

Writes the time from HH (hours) and MM (minutes) to the RTC time, and set seconds to zero. Note that DN (Day Number) is not written.

RTC documentation and tutorial

Dialect exclusions: Not available in dialects before 16.


Semaphore instructions

Note: The following is a short-form introduction to semaphores. The semaphore tutorial has much more information, especially if you are unfamiliar with "bits" and "bytes". Semaphores were introduced with dialect 12.

"Semaphore" is the term we use to describe a 1-bit variable. The semaphore instructions allow you to set, clear and test semaphores in a variety of ways. You can use all of user RAM as semaphores, so in a SPLat board with 128 bytes of RAM you have potentially 1024 semaphores.

Note: Semaphores are stored in, and are part of, SPLat's user RAM (formerly called Data Memory). You must take care not to accidentally use the same memory location for semaphores as well as for other types of data.

The word semaphore is a time-honoured name in programming. Other terms that have been used are "flag" or "switch". If your background is with relay logic you will be accustomed to "internal relay".

In SPLat instructions semaphores are addressed (selected) by a semaphore number 'ss' and a base address 'bb'. Both these are 8-bit numbers, so they can have values between 0 and 255.

The base address defines the byte address or location within RAM where the semaphore counting is to start. This can be any address within the existing RAM. The semaphore number is counted starting from the base address, with 8 semaphores per byte. Thus if the base address is 5, semaphore 0 will be bit 0 of location 5.

The base address is an optional part of every semaphore instruction (not a separate register). If you omit the base address it defaults to 0. Hence, it is perfectly possible to never specify the base address, in which case the first 32 bytes of RAM will become 256 semaphores.

In the indexed form, the index register is added to the base address, not to the semaphore number.

In This Section

ClrS ss+,bb* [D>=12]

GoIfSF ss,bb+,LLLL [D>=12]

GoIfST ss,bb+,LLLL [D>=12]

GoSubIfSF ss,bb+,LLLL [D>=12]

GoSubIfST ss,bb+,LLLL [D>=12]

NotS ss+,bb* [D>=12]

RecallS ss+,bb* [D>=12]

RetIfST ss,bb+ [D>=12]

RetIfSF ss,bb+ [D>=12]

SetS ss+,bb* [D>=12]

StoreS ss+,bb* [D>=12]

WaitForSF ss,bb [D>=16]

WaitForST ss,bb [D>=16]


Stack instructions

The register stack is the core data structure of the SPLat processor.

In This Section

LoadX nn

Pop

PopU n

PopV

Push

PushU n

PushV

Recall mm*+

RecallS ss+,bb* [D>=12]

RTCPopDM [D>=16]

RTCPopDN [D>=16]

RTCPopHH [D>=16]

RTCPopMM [D>=16]

RTCPopSS [D>=16]

RTCPushDM [D>=16]

RTCPushDN [D>=16]

RTCPushHH [D>=16]

RTCPushMM [D>=16]

RTCPushSS [D>=16]

Store mm*+

StoreS ss+,bb* [D>=12]

Swap


LoadX nn

Pushes the constant number nn into X.

Do not confuse with Recall


Pop

Pops all register stack data up one level, resulting in the following copying of data:

Y ==> X

Z ==> Y

T ==> Z

The original contents of X is lost and T is duplicated into Z

See also Push


PopU n

Copies X to U(n) and pops the stack.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


PopV

Copies X to V and pops the stack.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


Push

Pushes all register stack data down one level, resulting in the following copying of data:

Z ==> T

Y ==> Z

X ==> Y

The original contents of T is lost and the contents of X is duplicated.

See also Pop


PushU n

Pushes U(n) onto the stack.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


PushV

Pushes V onto the stack.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


Swap

Exchanges the contents of X and Y.


SPLat expansion framework generic instructions

The instructions in this category, are the "primitive" instructions that drive the SPLat Expansion Framework. It is extremely unlikely that you will need to use them or worry about them. These instructions are used by the programming software to "synthesise" instructions for SPLat peripherals, for example the LCD (OBLCD_ instructions). They are included here for completeness.

(When we make custom UV peripherals, it is less likely we will create high level instructions to match. In those cases we supply sample SPLat code using these primitives.)

In This Section

ClrU

fixToU n [D>=14]

floatFromU n [D>=14]

GoIfUF n,m,line

GoIfUT n,m,line

MemToUV m,n

PopU n

PopV

PushU n

PushV

QtoU n

RTCDateToUV [D>=16]

RTCDMToUV [D>=16]

RTCHHMMSSToUV [D>=16]

RTCHHMMToUV [D>=16]

SetU n,i

SetV i

SPiceConfigU

SPxChIn cc,aaaa [In]

SPxChOut cc,aaaa [Out]

SPxCmd0 cc,aaaa [Out]

SPxCmd1 cc,aaaa [Out]

SPxCmd2 cc,aaaa [Out]

SPxCmd4 cc,aaaa [Out]

SPxMode m

SPxPoll1 cc,aaaa [In]

SPxPoll2 cc,aaaa [In]

SPxPoll4 cc,aaaa [In]

SPxTxfrU p

StringToUV n,"String"

UtoQ n

UtoW n

UVToMem n,mm

WtoU n


ClrU

Resets the whole of U to zeros


fixToU n [D>=14]

Calculates the integer part of W and leaves it in U(n) - U(n+3) as a 24 bit signed number. This instruction is intended primarily for use with Tiny Serial Peripheral (TSP) data types Word and Long.

Although the result is in 32 bits the current implementation is limited to numbers in the range -8,388,607 to +8,388,607 (signed 2's complement 24-bit values, sign extended into the most significant byte).

Limiting cases:

  • If W contains values outside the range -8,388,607 to +8,388,607, the instruction returns the largest positive or negative 32-bit signed numbers numbers possible ( '7FFFFFFF or '80000000)
  • If W contains plus infinity or minus infinity, the instruction returns the largest positive or negative numbers possible ( '7FFFFFFF or '80000000).
  • If W contains NaN (not a number) it returns 0.

See also result codes

(This is in keeping with the principle of non-stop computing. It is your responsibility to check for extreme cases if anything bad might otherwise happen.)

Do not confuse with fix, which is the "classic" conversion of floating point in W to integer in X.

Dialect restriction: Available only in boards with dialect 14 or greater.

See also floatFromU


floatFromU n [D>=14]

Takes the signed integer in U(n) - U(n+3) and converts it to a floating point number in W. This instruction is intended primarily for use with Tiny Serial Peripheral (TSP) data types Byte, Word and Long.

Although the "input" number is in 32 bits, this instruction only converts numbers that will fit in 24 bits, i.e. numbers in the range -8,388,608 to +8,388,607. For numbers outside that range it will return + or - infinity.

See also result codes.

Dialect restriction: Available only in boards with dialect 14 or greater.

See also fixToU


MemToUV m,n

Copies a number of bytes from RAM to U. The starting point in RAM is location m. The starting point in U is U(n). The number of bytes copied is the number previously loaded into V.

The development environment will raise an error if m or n is invalid. The SPLat board will generate a fatal runtime error if the execution of the instruction runs past the end of RAM or U.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


QtoU n

Copies the 4 bytes of floating point register Q to U(n)...U(n+3). n must be in the range 0 to 16.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


SetU n,i

Set U(n) to the number i. i must be in the range 0-255.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


SetV i

Sets V to the number i. i must be in the range 0-255.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


SPiceConfigU

This is a special instruction associated with Touch of SPice boards.

SPiceConfigU

It is used where the SPice pins are configurable as to their functions. It uses the contents of U to configure the SPice pins. The number in U(0) configures the first pin, the number in U(1) configures the second pin etc. The values are:

0 = No change. The pins configuration remains unaltered

1 = Digital Output

2 = Digital Input

3 = Analog Input

4 = Analog Output or PWM output (see note below)

5 = Digital Bidirectional

6 = Counting Input

Other numbers could also apply. This mechanism is entirely a function of the individual SPLat board, so you need to refer to the documentation for the board you are using. The definition of pin numbers is also product dependent (but will not necessarily be the same as I/O numbers).

Note on configuration 4: Analog outputs on SPice boards are produced by generating an Pulse Width Modulated (PWM) logic signal in the processor and then filtering (averaging) it to give a DC analog voltage. Strictly speaking what appear on the SPice pin is therefore a PWM signal, but in many instances it wil appear to the user as an analog signal once it has been processed on a SPice board.

This is a low-level instruction associated with the SPLat Expansion Framework.


SPxChIn cc,aaaa [In]

This is called the Channel In instruction. It results in a variable number of bytes being sent by the peripheral and stored in U starting at U(0). The number of bytes transferred is determined by the peripheral, and will be between 0 and 20. The byte count is stored in V.

There are 16 allowable command codes, so this instruction could address 16 long source registers, for example display lines on a text display.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxChOut cc,aaaa [Out]

This is called the channel out instruction. It results in a variable number of bytes being sent to the peripheral. The data is taken from U starting at U(0). The number of bytes transferred is determined by V, and must be between 0 and 20.

There are 16 allowable command codes, so this instruction could address 16 destination registers, for example display lines on a text display. The channel out instruction was designed with LCD displays in mind, but will be useful wherever a variable data length is required or one that is more than 4 bytes long.

The peripheral will respond with a True or a False. This will be saved in U(0).

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxCmd0 cc,aaaa [Out]

This instruction results in 0 bytes of data being sent to the peripheral. This would be used either to send a simple command to the peripheral or to elicit a true/false response.

There are 128 command codes available with this instruction. Code 127 is reserved for a special purpose. That leaves codes 0-126 (127 possibilities).

This instruction is designed to provide simple trigger commands or on/off commands. For example, it could be used to provide on/off commands to up to 63 bits of output on a peripheral.

The peripheral will respond with a True or a False. This will be saved in U(0). This gives the possibility of polling up to 127 inputs or internal on/off semaphores in a peripheral.

Remember that it is entirely a function of the peripheral how it reacts to a command, and likewise the significance of the True/False response is a function of the peripheral. For example, in one peripheral the command may cause it to start a machine, while in another it may simply want it to report its state of readiness.

The instruction SPxCmd0 127,aaaa (i.e. with cc=127) has a special function. It is used as an "Are you there?" poll to peripheral aaaa. The peripheral must respond by sending back a True value, which is saved in U(0). If no peripheral responds, U(0) will be set to False. All other cases of a peripheral not responding to an SPx instruction result in a fatal run-rime error and full reset, i.e. the SPLat processor will turn off all outputs and start executing its program from the start.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxCmd1 cc,aaaa [Out]

This instruction results in 1 byte of data from U(0) being sent to the peripheral. There are 16 allowable command codes, so this instruction could address 16 1-byte registers, for example in a 16-digit display, or represent 16 different commands with 1-byte arguments.

The peripheral will respond with a True or a False. This will be saved in U(0).

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxCmd2 cc,aaaa [Out]

This instruction results in 2 bytes of data from U(0) and U(1) being sent to the peripheral. There are 16 allowable command codes, so this instruction could address 16 2-byte registers, for example in a multi-channel 16-bit counter.

The peripheral will respond with a True or a False. This will be saved in U(0).

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxCmd4 cc,aaaa [Out]

This instruction results in 4 bytes of data from U(0)-U(3) being sent to the peripheral. There are 16 allowable command codes, so this instruction could address 16 4-byte registers, for example floating point numbers.

The peripheral will respond with a True or a False. This will be saved in U(0).

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxMode m

Sets the clock rate and clock polarity for subsequent SPx and SPiceX transfers. The argument m determines the combination according to the following table:

m | Clock rate (Approx) | Clocks on edge ..
0 | 1.8MHz | Falling
1 | 920kHz | Falling
2 | 230kHz | Falling
3 | 58kHz | Falling
4 | 1.8MHz | Rising
5 | 920kHz | Rising
6 (default) | 230kHz | Rising
7 | 58kHz | Rising

In general, falling edge clocking suits peripherals with a microprocessor and rising edge clocking suits peripherals with hardware shift registers.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it unless you need to implement systems with more than about 70 I/O points. There is also a tutorial on SPiceX.


SPxPoll1 cc,aaaa [In]

This instruction results in 1 byte of data being polled out of the peripheral and saved in U(0). There are 16 allowable command codes, so this instruction could address 16 1-byte registers, for example in a 16-digit thumbwheel switch.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxPoll2 cc,aaaa [In]

This instruction results in 2 bytes of data being polled out of the peripheral and saved in U(0) and U(1). There are 16 allowable command codes, so this instruction could address 16 2-byte registers, for example in a 16-channel 16-bit counter.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxPoll4 cc,aaaa [In]

This instruction results in 4 bytes of data being polled out of the peripheral and saved in U(0)-U(3). There are 16 allowable command codes, so this instruction could address 16 4-byte registers, for example floating point numbers.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.

See also Error handling in SPx transfer instructions


SPxTxfrU p

Shift data out of U to the peripheral, and out of the peripheral into U. This provides a raw SPI transfer.

    SPxTxfrU 	p

p is the pin to be used as an enable/strobe and will be defined in the documentation for each peripheral. The instruction transfers V bytes of data from U, starting at U0, to the peripheral, and simultaneously reads back V bytes of data from the peripheral to U and leaves them in V contiguous bytes starting at U0.

For the output bytes, U0 will end up in the peripheral register electrically furthest from the controller board. For the input bytes, the peripheral register electrically closest to the controller board will end up in U0. This means the byte order between inputs and outputs is reversed. The bit order is not reversed.

Clock rate and polarity are as previously set in an SPxMode instruction.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it unless you need to implement systems with more than about 70 I/O points. There is also a tutorial on SPiceX.


StringToUV n,"String"

Loads "String" into U starting at U(n) and sets V to the string length.

"String" is a text string enclosed in quotation marks (double-quotes). n must be a zero or positive number no greater than 20 minus the length of the string.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


UtoQ n

Copies U(n)...U(n+3) to floating point register Q. n must be in the range 0 to 16.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


UtoW n

Copies U(n)...U(n+3) to floating point register W. n must be in the range 0 to 16.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


UVToMem n,mm

Copies a number of bytes from U to RAM. The starting point in U is U(n). The starting point in RAM is location mm. The number of bytes copied is the number previously loaded into V.

SPLat/PC will raise an error is mm or n is invalid. The SPLat board will generate a fatal runtime error if the execution of the instruction runs past the end of RAM or U.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


WtoU n

Copies the 4 bytes of floating point register W to U(n)...U(n+3). n must be in the range 0 to 16.

This is a low-level instruction associated with the SPLat Expansion Framework. You will most likely never need to know how to use it.


Timing instructions

These instruction are all related to measuring or generating time intervals.

In This Section

Blink oo+

fGetTimer t

fPulse0 [D>=19]

fSetTimer t

fSTSinceMark [D>=22]

fSTTimeSince aa+ [D>=16]

GetTick10 [D>=12]

GetTick100 [D>=12]

LoopIfTiming tttttt,LLLL [D>=16]

MarkTime [D>=16]

Pause tttt

Pulse0 tt [D= 5 to 15]

SetTimer t,dddd

Start t

Stop t

STScale ss [D>=16]

STStart aa+ [D>=16]

STTest aa+,tttttt [D>=16]

Test t

WaitOffT ii+,tttt

WaitOnKT ii+,tttt

WaitOnT ii+,tttt


GetTick10 [D>=12]

Read and decrements the 10mS time tick counter. The counter value is pushed into X.

The 10mS time tick counter is an 8-bit counter that counts up every 10mS, but will not count past 255. Each time it is read out with this instruction it is decremented by 1 but not past 0. This allows you to devise a variety of timing schemes. If your program is busy with something else for say 1second, when it gets back to testing for ticks there will be 1 second's worth of ticks (about 100) accumulated, so the next 100 GetTick10 instructions will return true. This allows you to create timing schemes that can "make up for lost time".

In SPLat/PC the simulator will simulate one 10mS tick for every 200 instructions simulated. This is a rough approximation to an average instruction execution time of 50uS. It is impossible to accurately simulate real-time.

This instruction is deprecated. Do not use in new programs.

See also STScale

Dialect exclusions: Not available in dialects before 12.


Pulse0 tt [D= 5 to 15]

Deleted in dialects 16 and later. It has been replaced by fPulse0

This instruction allows you to generate very precisely timed output pulses on output 0. Pulse widths from 1mS to 65,025 (255x255) can be generated.

Pulse0 pops the value (0-255) off the stack, multiplies it by the operand value tt (0-255) and starts a timer that turns output 0 On for the resulting number (0-65,025) of milliseconds. A Pulse0 instruction with a calculated duration of 0 will terminate any existing output pulse.

If the Pulse0 instruction is timing the output state will be on irrespective of any Output, On or Off instructions. When the Pulse0 instruction is not timing, or has timed out, the output state will be whatever it would have been had the Pulse0 instruction never been executed. We say that the Pulse0 instruction is ORed with the other output instructions.

Dialect restriction: The Pulse0 instruction is not implemented in SPLat hardware with dialect numbers less than D=5 or greater than 15 or which have no dialect number on the chip label.


SetTimer t,dddd

Set timer number t to time for duration dddd and start the timer counting. The timers count down towards zero, and stop at zero.

For timers 0-3, t is in units of 100mS. For timers 4-7, t is in units of 10mS. The maximum allowed value for t is 32,767. You may set the timer to 0, in which case it stops timing.

See also Start, Stop, Test and fSetTimer

These timers are a legacy feature and not recommended for new programs. The SuperTimers, along with Wait**T and Pause instructions provide a much more powerful mechanism.


Start t

Enables timer number t for counting after a previous Stop.

See also SetTimer, Stop, Test


Stop t

Prevents timer number t from counting (timing) until a subsequent Start or SetTimer instruction.

See also SetTimer, Start Test


Test t

Check timer t and Push result to X. If the timer is timed out (contains count zero) X becomes False (zero) otherwise X becomes True.

See also SetTimer and fGetTimer


Xwire instructions

Xwire allows several SPLat controllers and special purpose peripherals to be networked. Xwire documentation.

Previous Topic

Next Topic

In This Chapter

iFindXInBuf bb*,nn [D>=28]

iiChrPrintX dest# [D>=23]

iiGetHex bb#,nn [D>=28]

iifGetNum bb#,nn,fw [D>=28]

iifPrintWFW dest#,f,p [D>=23]

iifPrintWVW dest#,f,p [D>=23]

iiHexPrintX dest# [D>=23]

iiPrintFill dest#,count,value [D>=23]

iiPrintText dest#,text-string [D >= 23]

iisPrintXFW dest# [D>=23]

iisPrintXVW dest# [D>=23]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]

iiStrFind bb#,nn,c [D>=28]

XwireGetComErr [D>=24]

XwireGetErrCount [D>=20]

XwireGetJumpers [D>=20]

XwireGetPollCnt [D>=23]

XwireIdlePoll [D>=23]

XwireMaster pppp [D>=20]

XwirePhys nn [D>=28]

XwireSetAddr [D>=20]

XwireSlave pppp [D>=20]

XwireStop [D>=23]


XwireGetComErr [D>=24]

In an Xwire master, push the communications error counter into X and reset the count. This will give a count of the number of receive characters that have been corrupted, plus message CRC errors, since the previous time this instruction was executed. Generally this will indicate a noisy line or collisions between two slaves accidentally set to the same slave address.

XwireGetComErr is designed for detecting collisions (more than one slave responding to a poll) in systems where slave addresses may be dynamically set by a user. By comparison, XwireGetErrCount is most usefull for detecting missing slaves.

Xwire documentation

Dialect restriction: This instruction is not implemented on boards prior to dialect D=24.


XwireGetErrCount [D>=20]

In an Xwire master, push the error count into X and reset the count. This will give a count of how many Xwire polls have failed, due to all causes, since the previous time this instruction was executed.

Xwire documentation

Dialect restriction: This instruction is not implemented on boards prior to dialect D=20.


XwireGetJumpers [D>=20]

Some SPLat controllers contain jumpers intended for setting the Xwire address. This instruction reads the jumpers into X.

Note 1: The connection between the jumpers and the address is purely via the XwireSetAddr instruction. It is not automatic.

Note 2: If the jumpers are not used for setting the Xwire address they can be used for anything else. Your simply need to use XwireGetJumpers to read them as they do not appear as regular inputs.

Xwire documentation

Dialect restriction: This instruction is not implemented on boards prior to dialect D=20.


XwireGetPollCnt [D>=23]

Xwire maintains an 8-bit activity counter called the poll counter. This counter will count up to 255 then stop counting. It is reset whenever it is read out.

The XwireGetPollCnt instruction pushes the poll counter into X then resets the counter.

In an Xwire master

In an Xwire master the poll counter counts the number of times the program reaches the end of the master Xwire configuration table. Thus in the master it is an indicator of overall Xwire activity.

In an Xwire Slave

In an Xwire Slave the poll counter counts how many times the slave has been successfully polled by the master. Thus in a slave it can be used to establish that the slave is actually in communication with the master.

See also XwireGetErrCount


XwireIdlePoll [D>=23]

Returns a true (255) in X when Xwire polling has ceased following an XwireStop instruction.


XwireMaster pppp [D>=20]

Make this controller the Xwire master and use the configuration data table stored at address pppp in NVEM page 0.

Xwire documentation

Dialect restriction: This instruction is not implemented on boards prior to dialect D=20.


XwirePhys nn [D>=28]

On controllers with programmatically selectable serial interface, select which electrical interface to use. The argument nn makes the selection:

n = 0 gives the default interface (typically TTL)

n =1 gives the alternative interface (typically RS485)

Don't confuse this with port selection.

Xwire documentation

Dialect restriction: This instruction is not implemented on boards prior to dialect D=28.


XwireSetAddr [D>=20]

Use the value in X as the Xwire address of this controller. Pops X. Meaningless if this is not an XWire slave.

Xwire documentation

Dialect restriction: This instruction is not implemented on boards prior to dialect D=20.


XwireSlave pppp [D>=20]

Make this controller an Xwire slave and use the configuration data table stored at address pppp in NVEM page 0.

Xwire documentation

Dialect restriction: This instruction is not implemented on boards prior to dialect D=20.


XwireStop [D>=23]

In an Xwire master executing this instruction will turn off Xwire activity at the completion of the current Xwire transaction. This can be used in advanced schemes where you want to switch between various Xwire master configurations. This instruction can take some time (up to one complete poll/response sequence) to take effect, but does not hold up the processor.

See also XwireIdlePoll


WarmBoot [D .= 20]

Restarts the SPLat program "from the top" with all RAM set to 0, timers reset and all outputs off. This is identical to a power up reset, except how it is reported via the CPU device. In 32-bit controllers (EC1, HMI430 and future controllers) a WarmBoot results in a 10 second delay before the program re-starts. This gives you an opportunity to connect SPLat/PC to the controller. Older 8-bit controllers work differently, and don't need this delay.

Dialect exclusions: Not available in dialects before 20.

See also: CPU: Processor reset flags


Instructions arranged alphabetically

The alphabetical list contains all instructions. In the online and html help file views of this document, the lefthand contents list is a handy quick reference guide.

Previous Topic

Next Topic

In This Section

aCom_Flags pp [D>=29]

aComDevAddr pp [D>=29]

aComRx_BufLen pp [D>=29]

aComRx_DelXChars pp [D>=29]

aComRx_fGetNum pp,fw [D>=29]

aComRx_FindXInBuf (X) [D>=23]

aComRx_GetHex pp [D>=29]

aComRx_Peek pp [D>=29]

aComRx_ReadOne pp [D>=29]

aComRx_StrFind pp,c [D>=28]

aComRx_StrFind2 pp,c [D>=29]

aComRx_Trim pp [D>=29]

aComSetCCB pp,nnnn [D>=29]

aComTx_Space pp [D>=29]

ADC [D>=23]

ADD [D>=23]

And

AndM

AnIn#

AnOut#

Baud rate

Blink oo+

BlinkMask mm

Branch

BranchJ [D>=16]

BranchM mm+

BranchR

ClrInstCount [D>=18]

ClrS ss+,bb* [D>=12]

ClrU

Com_Flags [D>=23]

ComDevAddr [D>=17]

ComHaltScript [D>=17]

Compare

CompareR

ComResumeScript [D>=17]

ComRunScript pppp [D>=17]

ComRx_BufLen [D>=23]

ComRx_DelXChars [D>=23]

ComRx_fGetNum fw [D>=26]

ComRx_FindXInBuf (X) [D>=23]

ComRx_GetHex [D>=23]

ComRx_Peek (X) [D>=23]

ComRx_ReadOne [D>=23]

ComRx_StrFind c [D>=27]

ComRx_StrFind2 c [D>=29]

ComRx_Trim (X) [D>=23]

ComSetCCB pppp [D>=17]

ComTestStartTimer [D>=17]

ComTx_Space [D>=23]

DecI

DecM mm+

DecMGoIfNZ mm*+,LLLL

DecMRetIfNZ mm+

DecMRetIfZ mm+

DecX

defADDR Directive

defBLOCK directive

defBYTE Directive

defFLOAT Directive

defLONG Directive

defSEM Directive

defTIME24 Directive

defWORD Directive

DMGNZ mm*+,LLLL

EQU Directive

fAbs

fAdd

fAnIn c+ [D>=18]

fAnOut c+ [D>=18]

fEQU Directive

fCompareR

fDec

fDiv

fGetTimer t

fGoIfNeg LLLL

fGoIfNZ LLLL

fGoIfPos LLLL

fGoIfWGEQ LLLL [D>=23]

fGoIfWGTQ LLLL [D>=23]

fGoIfWLEQ LLLL [D>=23]

fGoIfWLTQ LLLL [D>=23]

fGoIfZ LLLL

fIEEEtoW [D>=21]

fIEEEtoWR [D>=21]

fInc

fix

FixToMem16S mm* [D>=21]

FixToMem16U mm* [D>=21]

fixToU n [D>=14]

fLoadQ ffff

fLoadW ffff

float

floatFromU n [D>=14]

FloatMem16S mm* [D>=21]

FloatMem16U mm* [D>=21]

fMul

fNeg

fPulse0 [D>=19]

fRecallQ mm*+

fRecallW mm*+

fSetTimer t

fSign

fStore mm*+

fSTSinceMark [D>=22]

fSTTimeSince aa+ [D>=16]

fSub

fSwap

fTest

fTestWeqQ [D>=24]

fTestWeqZ [D>=25]

fTestWgeQ [D>=25]

fTestWgeZ [D>=25]

fTestWgtQ [D>=25]

fTestWleQ [D>=25]

fTestWltQ [D>=25]

fTestWltZ [D>=25]

fTestWneQ [D>=25]

fTestWneZ [D>=25]

fWtoIEEE [D>=21]

fWtoIEEER [D>=21]

fWtoQ

GetCount7 [D= 4 to 15]

GetTick10 [D>=12]

GetTick100 [D>=12]

GoIfF LLLL

GoIfInK ii+,LLLL

GoIfInOff ii+,LLLL

GoIfInOn ii+,LLLL

GoIfINZ LLLL

GoIfIZ LLLL

GoIfMEQ mm+,nn,LLLL

GoIfMGE mm+,nn,LLLL

GoIfMGT mm+,nn,LLLL

GoIfMLE mm+,nn,LLLL

GoIfMLT mm+,nn,LLLL

GoIfMNE mm+,nn,LLLL

GoIfMNZ mm+,LLLL

GoIfMZ mm+,LLLL

GoIfNZ LLLL

GoIfRTCEvent e+,LLLL [D>=16]

GoIfSF ss,bb+,LLLL [D>=12]

GoIfSim LLLL

GoIfST ss,bb+,LLLL [D>=12]

GoIfT LLLL

GoIfUF n,m,line

GoIfUT n,m,line

GoIfXEQ nn,LLLL

GoIfXGE nn,LLLL

GoIfXGT nn,LLLL

GoIfXLE nn,LLLL

GoIfXLT nn,LLLL

GoIfXNE nn,LLLL

GoIfZ LLLL

GoSub LLLL

GoSubIfF LLLL

GoSubIfMNZ mm+,LLLL

GoSubIfMZ mm+,LLLL

GoSubIfNZ LLLL

GoSubIfSF ss,bb+,LLLL [D>=12]

GoSubIfST ss,bb+,LLLL [D>=12]

GoSubIfT LLLL

GoSubIfXEQ nn,LLLL

GoSubIfXGE nn,LLLL

GoSubIfXGT nn,LLLL

GoSubIfXLE nn,LLLL

GoSubIfXLT nn,LLLL

GoSubIfXNE nn,LLLL

GoSubIfZ LLLL

GoTo LLLL

IasJ: Precode [D>=16]

iEQU Directive

iFindXInBuf bb*,nn [D>=28]

iiChrPrintX dest# [D>=23]

iifGetNum bb#,nn,fw [D>=28]

iiGetHex bb#,nn [D>=28]

iifPrintWFW dest#,f,p [D>=23]

iifPrintWVW dest#,f,p [D>=23]

iiHexPrintX dest# [D>=23]

iiPrintFill dest#,count,value [D>=23]

iiPrintNVText dest# [D>=27]

iiPrintText dest#,text-string [D >= 23]

iisPrintXFW dest# [D>=23]

iisPrintXVW dest# [D>=23]

iiStrFind bb#,nn,c [D>=28]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]

IncI

IncM mm+

IncX

Input ii*+

InputF ii*+

InputFM ii+

InputK ii*+

InputO ii*+

InputOM ii+ [D>=13]

InputR ii+,tt

ItoX

KBeep [D>=16]

KBeepOff

KBeepOn

KillTask [D>=16]

LaunchTask LLLL,jj [D>=16]

LaunchTaskJ LLLL [D>=16]

LaunchTaskX LLLL [D>=16]

LoadI nn

LoadX nn

LoopIfTiming tttttt,LLLL [D>=16]

MarkTime [D>=16]

MemToUV m,n

mEQU Directive

NoJ: Precode [D>=16]

Not

NotM

NotS ss+,bb* [D>=12]

NV0Byte Directive [D>=15]

NV0fNum Directive [D>=15]

NV0Ptr Directive [D>=15]

NV0Space Directive [D>=15]

NVAddPtr nn [D>=15]

NVAdvPtr [D>=15]

NVDecRecNum [D>=15]

NVEM0 Directive [D>=15]

NVfGetEndW [D>=15]

NVfGetPtrW [D>=15]

NVfGetRecNumW [D>=15]

NVfPutPtrW [D>=15]

NVfPutRecNumW [D>=15]

NVfReadQ nn*+ [D>=15]

NVfReadW nn*+ [D>=15]

NVfWriteW nn*+ [D>=15]

NVIncRecNum [D>=15]

NVPopByte nn*+ [D>=15]

NVPopPage [D>=15]

NVPopRecLen [D>=15]

NVPopRecNum [D>=15]

NVPushByte nn*+ [D>=15]

NVPushPage [D>=15]

NVPushRecLen [D>=15]

NVPushRecNum [D>=15]

NVReadRec mm [D>=15]

NVReadToPtr nn [D>=15]

NVSetPage nn [D>=15]

NVSetPtr pppp [D>=15]

NVSetRecLen nn [D>=15]

NVSetRecNum nnnn [D>=15]

NVSubPtr nn [D>=15]

NVtoUV nn,u [D>=15]

NVWriteRec mm [D>=15]

OBCA_Clr

OBCA_fRdClr

OBCA_fRead

OBCA_GoIfNoRdg line

OBCA_GoIfRdg line

OBCA_GoSubIfRdg line

OBCA_Mode m

OBCA_RetIfNoRdg

OBCA_WaitRdg

OBCB_fRdClr c [D>=19]

OBCB_fRead c [D>=19]

OBCB_GoIfA l

OBCB_GoIfB LLLL [D>=19]

OBCB_Start c [D>=19]

OBCB_StartA c [D>=19]

OBCB_Stop c [D>=19]

OBCB_StopB c [D>=19]

OBLCD_CharX [D>=

OBLCD_Cls

OBLCD_CurBlink

OBLCD_CurOff

OBLCD_CurOn

OBLCD_Date [D>=16]

OBLCD_Dim Lo,Hi,Delay

OBLCD_DM [D>=16]

OBLCD_fDispW f,p

OBLCD_GetCurYX [D>=16]

OBLCD_GoIfBusy line

OBLCD_GoIfIdle line

OBLCD_HexDispX

OBLCD_HGraph [D>=18]

OBLCD_HHMM [D>=16]

OBLCD_HHMMSS [D>=16]

OBLCD_NVText nn [D>=15]

OBLCD_RUT

OBLCD_SDecDispMFW mm

OBLCD_SDecDispMVW mm

OBLCD_SDecDispXFW

OBLCD_SDecDispXVW

OBLCD_SetCur r,c

OBLCD_SetCurYX [D>=16]

OBLCD_SpclChar c

OBLCD_Text "Message"

OBLCD_Type tt

OBLCD_UDecDispMFW mm

OBLCD_UDecDispMVW mm

OBLCD_UDecDispXFW

OBLCD_UDecDispXVW

OBLCD_WaitIdle

OBQC_Clr c [D>=16]

OBQC_fGet c [D>=16]

OBQC_fSet c [D>=16]

OBQC_Status c [D>=17]

oEQU Directive

Off oo*+

On oo*+

Or

OrM

Output oo*+

OutputB oo+

OutputM oo+

Pause tttt

PermRecall [D>=2]

PermStore [D>=2]

Pop

PopU n

PopV

Pulse0 tt [D= 5 to 15]

Push

PushU n

PushV

QtoU n

Recall mm*+

RecallS ss+,bb* [D>=12]

ResetK

Resume mm*+ [D>=12/15]

RetIfF

RetIfMNZ mm+

RetIfMZ mm+

RetIfNZ

RetIfSF ss,bb+ [D>=12]

RetIfST ss,bb+ [D>=12]

RetIfT

RetIfZ

Return

RolM

RorM

RTCClrDM [D>=16]

RTCDateToUV [D>=16]

RTCDecDN [D>=25]

RTCDecHH [D>=25]

RTCDecMM [D>=25]

RTCDecSS [D>=25]

RTCDMToUV [D>=16]

RTCFailFlag [D>=20]

RTCHHMMSSToUV [D>=16]

RTCHHMMToUV [D>=16]

RTCIncDN [D>=16]

RTCIncHH [D>=16]

RTCIncMM [D>=16]

RTCIncSS [D>=16]

RTCNotDM [D>=16]

RTCPopDM [D>=16]

RTCPopDN [D>=16]

RTCPopHH [D>=16]

RTCPopMM [D>=16]

RTCPopSS [D>=16]

RTCPushDM [D>=16]

RTCPushDN [D>=16]

RTCPushHH [D>=16]

RTCPushMM [D>=16]

RTCPushSS [D>=16]

RTCReadDate [D>=16]

RTCReadEvent e+ [D>=16]

RTCReadTime [D>=16]

RTCSetDM [D>=16]

RTCTestEvent e+ [D>=16]

RTCWriteDate [D>=16]

RTCWriteDN [D>=16]

RTCWriteEvent e+ [D>=16]

RTCWriteTime [D>=16]

RtoX [D>=23]

RunTasks [D>=16]

RunTasksForever [D>=16]

SBC [D>=23]

sEQU Directive [D>=12]

SetCount7 [D= 4 to 15]

SetMem mm*+,nn

SetS ss+,bb* [D>=12]

SetTimer t,dddd

SetU n,i

SetV i

ShadowClear mm*+,cc

ShadowRead mm*+,cc

ShadowWrite mm*+,cc

SPiceConfigU

SPxChIn cc,aaaa [In]

SPxChOut cc,aaaa [Out]

SPxCmd0 cc,aaaa [Out]

SPxCmd1 cc,aaaa [Out]

SPxCmd2 cc,aaaa [Out]

SPxCmd4 cc,aaaa [Out]

SPxMode m

SPxPoll1 cc,aaaa [In]

SPxPoll2 cc,aaaa [In]

SPxPoll4 cc,aaaa [In]

SPxTxfrU p

Start t

Stop t

Store mm*+

StoreS ss+,bb* [D>=12]

StringToUV n,"String"

STScale ss [D>=16]

STStart aa+ [D>=16]

STTest aa+,tttttt [D>=16]

SUB [D>=23]

Suspend mm*+ [D>=12/15]

Swap

Target LLLL

Test t

TestXeqY [D>=28]

TestXgeY [D>=28]

TestXgtY [D>=28]

TestXleY [D>=28]

TestXltY [D>=28]

TestXneY [D>=28]

UtoQ n

UtoW n

UVToMem n,mm

UVtoNV nn,u [D>=15]

WaitForRTCEvent e+ [D>=16]

WaitForSF ss,bb [D>=16]

WaitForST ss,bb [D>=16]

WaitOff ii+

WaitOffT ii+,tttt

WaitOn ii+

WaitOnK ii+

WaitOnKT ii+,tttt

WaitOnT ii+,tttt

WarmBoot [D .= 20]

WtoU n

Xor

XorM

XtoI

XwireGetComErr [D>=24]

XwireGetErrCount [D>=20]

XwireGetJumpers [D>=20]

XwireGetPollCnt [D>=23]

XwireIdlePoll [D>=23]

XwireMaster pppp [D>=20]

XwirePhys nn [D>=28]

XwireSetAddr [D>=20]

XwireSlave pppp [D>=20]

XwireStop [D>=23]

YieldTask [D>=16]

# 4D_ViSi hash command - MultiTrack+ only!

# 4D_bWriteObj(ObjType, ObjIndex, aa) hash function

# 4D_fWriteObj(ObjType, ObjIndex, ff) hash function

# 4D_fWriteObjW(ObjType, ObjIndex) hash function

# 4D_NackEvent(Handler) hash function

# 4D_ObjEvent(ObjType, ObjIndex, Handler) hash function

# 4D_SetFormX() hash function

# 4D_WriteContrastX() hash function

# 4D_WriteObj(ObjType, ObjIndex, Value) hash function

# 4D_WriteObjX(ObjType, ObjIndex) hash function

# ButtonEvent(ID, Row, Col, Height, Width, "text"{, Mode}, ClickHandler) hash function

# ButtonParams(btn, row, col, height, width, "text", visible, ClickHandler) hash function

# Cls() hash function

# ColourWheelEvent(ev:EventHandler) hash function

# ConnectEvent(ev:EventHandler) hash function

# CursorRel(r|y:dRow, c|x:dColumn) hash function

# Display(Bits) hash function

# Enables(Mask) hash function

# fDispW(f, p) hash function

# Feed(Vfeed, Vfs, Rfeed) hash function

# File(FileName) hash function

# FileOpen(FileName, Mode) hash function

# FloatVar(fArg, f, p) hash function

# GetRTC() hash function

# Hash commands and functions

# HBarOld(Row, Col, Height, FSWidth, fArg) hash function

# HexVar(bArg) hash function

# HHMMSS() hash function

# HideAllButtons() hash function

# HMI hash command

# HMI_Log hash command

# HMI_Trace hash command

# Include hash command

# IntInputEvent(t|p:"Prompt"{, L:Min}{, h:Max}, ev:EventHandler) hash function

# LogWrite() hash function

# ModBusMaster(BaudRate, Parity, MessageGap) hash function

# ModBusSlave(BaudRate, Parity, Address, MessageGap) hash function

# NewFile(FileName) hash function

# NL() hash function

# Open_Serial hash command

# Order(o) hash function

# Params(R25, Beta) hash function

# PopBGColor() hash function

# PopColors() hash function

# PopFGColor() hash function

# Port(p) hash function

# PushColors() hash function

# RangeC(Tmin, Tmax) hash function

# Reset({i:BGImage}{, b:BGColour}) hash function

# Rulers() hash function

# SetBGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# SetCursor(r|y:Row, c|x:Col) hash function

# SetFGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# SetImagePath(PathName)

# SetRTCEvent({EventHandler}) hash function

# ScreenWidthEvent(c:C, ev:EventHandler) hash function

# OldSpeakNumb(fNumb, f, p)

# SpeakNumb(n:Number{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

# SpeakText (t:Text{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

# SPLatLink(BaudRate) hash function

# Print(Arg1{, Arg2 ...}) hash function

# Thermistor hash command

# Theta(Rt) hash function

# TraceOff() hash function

# TraceOn() hash function

# uDispXFW() hash function

# uDispXVW() hash function

# User(BaudRate, Bits, Parity) hash function

# YYMMDD() hash function


Baud rate

This is a Communication Control Block (CCB) parameter. It occupies byte 3 (counting from 0) in the CCB.

0 | 300
1 | 600
2 | 1200
3 | 2400
4 | 4800
5 | 9600
6 | 19200
7 | 38400

Applies to the following protocols: All but SPLatLink, which is always 9600.


defADDR Directive

The defADDR directive directs SPLat/PC to assign a value to a label and reserve a nominated amount of RAM to hold the data associated with that label. It is part of the Automatic RAM Allocation scheme introduced in SPLat/PC V7.15.2 (May2005).

Syntax:

Label     defADDR      {Value}

Label is any properly formed label. Value is an optional numeric constant. If not used Value defaults to 1. Typically you would specify Value if you intend to access an array of data using indexed addressing.

This will automatically allocate 2x Value bytes of RAM and assign the address of the first byte to Label.

This directive is intended for reserving a Suspend/Resume address in multitasking structures.

An error will be raised if SPLat/PC is unable to find enough free RAM.


defBLOCK directive

The defBLOCK directive forces SPLat/PC to reserve a contiguous block of RAM for the subsequent defBYTE, defFLOAT, defLONG, defTIME24 or defWORD memory allocations (but NOT defSEM). It is available in SPLat/PC V8.20.2 and later.

Syntax:

          defBLOCK      Value

Value is a required numeric constant or text defined with #EQU.

The next "Value" bytes worth of automatically allocated RAM will be allocated in an unbroken block.

Example:

When using Xwire or ModBus master you generally need to set aside a contiguous block of RAM for exchanging data with a remote device. The code to do that might look like this:

           defBLOCK       10     ;10 bytes of data sent out on Xwire
XW_Float1  defFLOAT              ;4 bytes
XW_Byte1   defBYTE               ;1 byte
XW_Float2  defFLOAT              ;4 bytes
XW_Sems    defBYTE               ;1 byte   = 10 bytes total

The "defBLOCK 10" guarantees that the next 4 RAM data items (occupying 10 bytes) will be allocated in a single, unbroken block of RAM. In the Xwire configuration table you would use XW_Float1 to identify the data area.

An error will be raised if SPLat/PC is unable to find enough free RAM.

Note: The associated RAM allocations must occupy the exact same number of bytes as the defBLOCK reserves. If you try to have more RAM bytes in the block, the results will be unpredictable.


defBYTE Directive

The defBYTE directive directs SPLat/PC to assign a value to a label and reserve a nominated amount of RAM to hold the data associated with that label. It is part of the Automatic RAM Allocation scheme introduced in SPLat/PC V7.15.2 (May2005).

Syntax:

Label     defBYTE      {Value}

Label is any properly formed label. Value is an optional numeric constant. If not used Value defaults to 1. Typically you would specify Value if you intend to access an array of data using indexed addressing.

This will automatically allocate Value bytes of RAM and assign the address of the first byte to Label.

An error will be raised if SPLat/PC is unable to find enough free RAM.


defFLOAT Directive

The defFLOAT directive directs SPLat/PC to assign a value to a label and reserve a nominated amount of RAM to hold the data associated with that label. It is part of the Automatic RAM Allocation scheme introduced in SPLat/PC V7.15.2 (May2005).

Syntax:

Label     defFLOAT      {Value}

Label is any properly formed label. Value is an optional numeric constant. If not used Value defaults to 1. Typically you would specify Value if you intend to access an array of data using indexed addressing.

This will automatically allocate 4x Value bytes of RAM and assign the address of the first byte to Label.

An error will be raised if SPLat/PC is unable to find enough free RAM.


defLONG Directive

The defLONG directive directs SPLat/PC to assign a value to a label and reserve a nominated amount of RAM to hold the data associated with that label. It is part of the Automatic RAM Allocation scheme introduced in SPLat/PC V7.15.2 (May2005).

Syntax:

Label     defLONG      {Value}

Label is any properly formed label. Value is an optional numeric constant. If not used Value defaults to 1. Typically you would specify Value if you intend to access an array of data using indexed addressing.

This will automatically allocate 4x Value bytes of RAM and assign the address of the first byte to Label.

An error will be raised if SPLat/PC is unable to find enough free RAM.


defSEM Directive

The defSEM directive directs SPLat/PC to assign a value to a label and reserve single bit of RAM to hold the semaphore (optionally multiple semaphores) associated with that label. It is part of the Automatic RAM Allocation scheme introduced in SPLat/PC V7.15.2 (May2005).

Syntax:

Label     defSEM      {Value}

Label is any properly formed label. Value is an optional numeric constant. If not used Value defaults to 1. Typically you would specify Value if you intend to access an array of data using jndexed addressing.

SPLat/PC will allocate one byte of RAM for every 8 semaphores.

An error will be raised if SPLat/PC is unable to find enough free RAM.


defTIME24 Directive

The defTIME24 directive directs SPLat/PC to assign a value to a label and reserve a nominated amount of RAM to hold the data associated with that label. It is part of the Automatic RAM Allocation scheme introduced in SPLat/PC V7.15.2 (May2005).

Syntax:

Label     defTIME24      {Value}

Label is a line label. Value is an optional numeric constant. If not used Value defaults to 1. Typically you would specify Value if you intend to access an array of data using indexed or jndexed addressing.

This will automatically allocate 3x Value bytes (24 bits) of RAM and assign the address of the first byte to Label.

An error will be raised if SPLat/PC is unable to find enough free RAM.


defWORD Directive

The defWORD directive directs SPLat/PC to assign a value to a label and reserve a nominated amount of RAM to hold the data associated with that label. It is part of the Automatic RAM Allocation scheme introduced in SPLat/PC V7.15.2 (May2005).

Syntax:

Label     defWORD      {Value}

Label is any properly formed label. Value is an optional numeric constant. If not used Value defaults to 1. Typically you would specify Value if you intend to access an array of data using indexed addressing.

This will automatically allocate 2x Value bytes of RAM and assign the address of the first byte to Label.

An error will be raised if SPLat/PC is unable to find enough free RAM.


EQU Directive

The EQU directive assigns a numeric value to a label.

Syntax:

Label     EQU      Value

Label is any properly formed label. Value must be an integer numeric constant. Value must be in a number range that is legal for a subsequent instruction where Label is used as an argument.

Foo       EQU      35

SPLat/PC will use the numeric value 35 wherever the word "Foo" is used as an instruction argument, hence subsequent to the above instruction

          LoadX    Foo

is equivalent to

          LoadX    35

The advantage of using EQU directives is that a numeric value can be defined once at the start of a program and then be used several times throughout the program. If you subsequently need to change the value, you only need to change the EQU directive and all uses of the value will change automatically.

The full word EQUALS may also be used.

See also #EQU, fEQU, iEQU, oEQU, mEQU


fEQU Directive

The fEQU directive assigns a floating pint value to a label.

Syntax:

Label     fEQU      Value

Label is any properly formed label. Value must be an floating point numeric constant. Example

fPi       fEQU      3.1415926

SPLat/PC will use the numeric value 3.1415926 wherever the word "fPi" is used as an argument in a floating point instruction, hence subsequent to the above instruction

          fLoadW    Pi

is equivalent to

          fLoadW    3.1415926

The advantage of using fEQU directives is that a floating point constant value can be defined once at the start of a program and then be used several times throughout the program. If you subsequently need to change the value, you only need to change the fEQU directive and all uses of the value will change automatically.


IasJ: Precode [D>=16]

The IasJ: precode forces all instructions that contain 8-bit resource addresses to apply the index register I to the address argument using "J rules". It is primarily designed to allow MultiTrack programs to cross over between addressing modes, but has the spin-off consequence that nearly all memory and I/O instructions can now be indexed.


iEQU Directive

The iEQU directive is a special variant of the EQU directive intended for defining input numbers.

Example

FullSwitch   iEQU    5

The difference between iEQU and EQU is that SPLat/PC will use information gathered from iEQU directives to add label information to the I/O window. As you move the mouse across the inputs the corresponding labels will appear in the title bar of the window, providing SPLat/PC has scanned the program line (as will be the case during simulation or single stepping).

By using this directive your program becomes easier to read. It also allows you to re-assign I/O points easily.

Note: Future programming software will have likely have stronger "typing". That means the software will raise an error if a label defined with the iEQU directive is used in other than an I/O instruction.


InputR ii+,tt

Produces a True result in X if input ii is on when the instruction is first executed and remains on for the next tt x 10mS. Otherwise returns a False result in X. This is used for generating auto-repeat keys, for example for LCD based menu systems.

tt must be between 1 and 255.

+ From dialect 16 the address argument will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode.

Dialect exclusions: Not available in dialects before 9.

From dialect 20 some controllers allow you to program the 10mS basic "tick" interval over the range 1mS to 255mS via a setting in the CPU device. See the CPU device documentation for your controller. This will affect the tt argument in InputR.


mEQU Directive

The mEQU directive is a special variant of the EQU directive intended for defining RAM (data memory) locations.

There are two forms of the mEQU directive:

"Classic form"

Example:

Counter:     mEQU    27

In this form the label Counter is defined to have the numeric value 27, so any subsequent use of Counter as an instruction argument will substitute the number 27.

"Extended form"

Example:

Counter:     mEQU    27,6

In this form there is a second optional argument, '6'. The label Counter is defined to have the numeric value 27 and 6 bytes of RAM will be reserved (and hence not available for automatic RAM allocation.)

The "classic" form, i.e. with the optional second argument omitted, will reserve 4 bytes of RAM by default (corresponding to a floating point variable).

Using mEQU is called manual RAM allocation. You work out exactly where you want your RAM variables to be located. Note that it is perfectly legal to overlap the RAM addresses and blocks declared in mEQU directives and SPLat/PC will not raise any errors. However, you should do that only if you understand the ramifications of having several variables share the same RAM.

As an aid to debugging mEQU adds label information to the SPLat/PC Data Memory window. As you move the mouse across the memory locations the corresponding labels will appear in the title bar of the window, providing SPLat/PC has scanned the program line (as will be the case during simulation or single stepping).


NoJ: Precode [D>=16]

When placed in front of an instruction, prevents that instruction from being jndexed. This is meaningful only inside a MultiTrack task.


OBLCD_WaitIdle

Wait for the LCD to become idle (not busy). This is not usually required as an explicit instruction, because all LCD instructions automatically wait for the LCD to become idle before proceeding.

Note: This instruction modifies UV

Dialect exclusions: Not available in dialects before 9. Also, not available on SPLat controllers that do not have onboard LCD support (please check the documentation for your specific board and Firmware version).


oEQU Directive

The oEQU directive is a special variant of the EQU directive intended for defining output numbers.

Example

AlarmLamp   oEQU    15

The difference between oEQU and EQU is that SPLat/PC will use information gathered from oEQU directives to add label information to the I/O window. As you move the mouse across the outputs the corresponding labels will appear in the title bar of the window, providing SPLat/PC has scanned the program line (as will be the case during simulation or single stepping).

By using this directive your program becomes easier to read. It also allows you to re-assign I/O points easily.

Note: Future programming software will have likely have stronger "typing". That means the software will raise an error if a label defined with the oEQU directive is used in other than an I/O instruction.


RtoX [D>=23]

Pushes the R register into X.

See also ADD, SUB, ADC, SBC


SBC [D>=23]

Binary 8-bit subtraction of X from Y with carry in. Pops the operands off the stack, leaves the result in X. If the subtraction produces a borrow, sets the carry flag.

The R register does double duty as result flags for binary arithmetic instructions.

From dialect 23 bit 0 of R is the carry flag. A carry out of an ADD, ADC, SUB or SBC instruction will set bit 0 of R.

The same bit 0 the acts as the carry input to ADC and SBC.

See also ADD, SUB, ADC, RtoX


sEQU Directive [D>=12]

Deprecated!

The sEQU directive has no special significance in SPLat/PC, and is treated identically to EQU.


Target LLLL

One or more lines following a Branch instruction must contain Target followed by a program line label LLLL.

See also Branch BranchM BranchR ComBranchScriptStatus


UVtoNV nn,u [D>=15]

Copy V to RecLen. Then copy V bytes from U(u) to NVEM at Pointer + RecLen x RecNum + nn. Raise a fatal error if it runs off the end of NVEM or U.

Dialect exclusions: Not available in dialects before 15.


# 4D_ViSi hash command - MultiTrack+ only!

The 4D_ViSi hash command gives you a very easy way of programming a suitable SPLat board to interact with touch screen displays from 4D Systems that have been set up using 4D's ViSi-Genie programming environment. The ViSi-Genie programming environment makes it very easy to design screen layouts with buttons, instruments and other elements.

To understand this material you need to be thoroughly familiar with how the 4D ViSi-Genie system works. Please study 4D Systems' documentation.

The programming model for ViSi-Genie is based on having several "forms" (screen layouts) that can each contain one or more "objects". Objects are things like gauges and buttons. You design your form layouts, and specify the properties of the objects, using the ViSi-Genie programming environment.

The SPLat can interact with forms and objects via hash functions.

  • Objects can be sent data from the SPLat, for example the position of a gauge or the on/off value of an LED indicator.
  • Objects can also report events back to the SPLat, for example the press of a graphical button or a change in the setting of a graphical slider bar.
  • The display can be sent commands from the SPLat to display a different form (screen layout) or alter its contrast setting.

Can only be used in a MultiTrack+ task


# ButtonParams(btn, row, col, height, width, "text", visible, ClickHandler) hash function

DEPRECATED

Valid for use with hash command: HMI

Don't use this without good reason. Use ButtonEvent in preference.

Change the settings of a button. All the parameters except btn are optional. To omit a parameter have the comma but nothing else.

This function is useful for dynamically changing a button's appearance without having to specify a different click handler. It makes sense only if a button is already displayed via a ButtonEvent function.

The parameters are:

btn | The button number to use. The button must have been initially displayed and explicitly numbered with a ButtonEvent.
Row | The screen row where the top of the button is to be positioned. This refers to character rows in the default font size. Row numbers are 0 based, so the top row is row 0.
Col | The screen column where the left of the button is to be positioned. This refers to character columns in the default font size. Column numbers are 0 based, so the start of each line is 0.
Height | The height of the button in multiples of the character height of the default font.
Width | The width of the button in multiples of the character width of the default font.
Visible | If this is 0, the button will be invisible. Any non-zero value makes the button visible.
"text" | The text to be displayed on the button. This must be enclosed in quotation marks. Non-printing characters are not possible
ClickCode | The actual code that will be sent by the HMI when the button is clicked. It is highly advisable not to change this!

In addition to being simple numbers, the row/column coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a row specifier | As a column specifier
5 | Row number 5 from the top, counting from 0. This can be a fractional number such as 5.5 | Column number 5 from the left, counting from 0
-5 | Row 5 from the bottom, counting from 0. This can be a fractional number such as -5.25 | Column 5 from the right, counting from 0
0 | The top character row | The left-most character column
-0 | The bottom character row | The right-most character column
0.25 | One quarter of (0.25 times) the screen height from the top. Fractional number between -1 and +1 are interpreted as a fraction of the screen. | One quarter of the screen width from the left.
-0.25 | One quarter of the screen height from the bottom. | One quarter of the screen width from the right.
C | The top-bottom centre of the screen. | The left-right centre of the screen.
C+5 | Five character rows down from the centre | Five character widths right from the centre.
C-5 | Five character rows up from the centre | Five character width left from the centre.
C+.25 | One quarter character row down from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | One quarter character width right from the centre.
C-3.25 | Three and a quarter character rows up from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | Three and a quarter character widths left from the centre.

In addition to being simple numbers, the width and height coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a height specifier | As a width specifier
5 | 5 character rows in height | 5 character rows in width
0.25 | One quarter of (0.25 times) the screen height. Always calculated as a full number of rows, always rounded down, but forced to be at least 1. | One quarter of (0.25 times) the screen width. Always calculated as a full number of columns, always rounded down, but forced to be at least 1.

See also ButtonEvent for a higher level of functionality.


# Hash commands and functions

Hash commands and hash functions were introduced in January 2012. They are supported in SPLat/PC Build 160 or later.

A hash command is written as a line that starts with a hash character, #, also called sharp or pound sign.

Example:

#	HMI	SetCursor(3, 5)

In many hash functions the arguments can also be given as Name:Value Pairs (NVP) :-

#	HMI	SetCursor(r:3, c:5)

In the above line of code the # in column 1 signals SPLat/PC to process the line as a hash command rather than a regular SPLat language instruction. In this particular example the hash command is HMI, while SetCursor(3, 5) is what we call a hash function. (The distinction between "command" and "function" is important).

One hash command may have zero, one or more hash functions on the same line. This may and will vary between hash commands. Also, each hash command will have its own unique set of allowable hash functions.

Note that the # must be the first non-space (or tab) character on the line. Therefore you can't label a hash command line. You can however have a label on the line above and get the same effect.

This section is organized by hash command. Under each hash command you will find all the matching hash functions. The headings all start with a # character so they will all appear together in the index.


# HMI hash command

The HMI hash command is used to control a SPLat "Simple HMI". This can be either the SimpleHMI function built into SPLat/PC, or the SimpleHMI Android (smartphone) app. In either case the SPLat program is identical, so whatever works inside SPLat/PC will also work when an Android device is used as an HMI.

Example:

# HMI    Cursor(5,0) Text("Hello World")

will place the HMI cursor at row 5, column 0 and display the text "Hello World" (providing the serial port has been suitably initialized and the required connection to an HMI device exists).

In the above code line Cursor and Text are hash functions that specify what to do with the HMI. All the hash functions that are possible with the HMI hash command are listed in this section.

Event handlers

Some of the HMI-related hash functions include a parameter for specifying "Event handlers". Example:

# HMI    ButtonEvent(, 3, 4, 5, 10, "Test Button", MyButtonEvent)

When the SPLat controller executes this hash command, it will display button number 1 on the HMI at row 3, column 4, with height 5 and width 10. The label on the button will be "Test button". When an Enduser clicks the button, the SPLat will automatically execute a GoSub MyButtonEvent. It is then up to you to have a subroutine at a line labelled MyButtonEvent which does whatever you want to do when the user clicks the button, then executes a Return. That subroutine is called an Event handler, because it "handles" the "button_click event".

Event handlers are possible only if your program uses MultiTrack. The feature uses just one MultiTrack task.

SimpleHMI Tutorial


# HMI_Log hash command

The HMI_Log hash command accesses a versatile data logging capability within SimpleHMI. You can easily generate log files in any text format, for example plain text or CSV. CSV is a defacto standard for importing into spreadsheets and data bases.

Note that in SPLat/PC the SIO/SimpleHMI window must be active for logging to take place. A file path (folder name) must be specified for the log file, in the Settings tab of the SIO window. The file name itself is embedded in the SPLat program.


# HMI_Trace hash command

The SimpleHMI function provided in SPLat/PC includes a trace feature, intended primarily as a tool for debugging of your SPLat program. This is not supported in the Android version of SimpleHMI, because it doesn't fit so well with the Android use cases.

The regular SimpleHMI function lets you manipulate the screen layout, and have for example a spot on the screen that shows the latest temperature reading. Each time the temperature is updated the previous value is lost. The trace function, by contrast, provides a scrolling screen that gives a running record of what has happened, so it could show the last 10, or 100, or 1000 temperature readings. The trace function also lets you save data to disk for later review.

The trace screen only appears if you click the Show Trace button.


# Include hash command

The Include hash command lets you include one SPLat source files within another. The effect is the same as manually copying and pasting the contents of one file into another.

Example:

Suppose you have a file named washington.spt that looks like this:

;----- Important stuff happens here ----
Potus:	  defBYTE
No1600:    Recall    Potus
           Return

Then in your main file you have:

....
; Find the value of Potus
           GoSub     No1600
...
...
# Include File(washington.spt)

When your main file is translated by SPLat/PC, the line

# Include File(washington.spt)

gets replaced by the entire contents of the file washington.spt . It is as if you had copied and pasted one file into another. You effectively end up with:

....

; Find the value of Potus

           GoSub     No1600

...

...

;# Include File(washington.spt)

;----- Important stuff happens here ----

Potus:	  defBYTE

No1600:    Recall    Potus

           Return

This is a very useful way of managing projects, and of keeping you favourite routines and code snippets in a library.

You can have any number of Include hash commands in a file. You can even have includes within included files. Included files can be nested 10 deep. You cannot have recursive inclusions, such as a file including itself or two files that include each other as this will violate the allowed nesting depth.

The Include hash command gives you a powerful mechanism for building and using libraries.

You cannot single step or simulate any hash command, including the Include hash command. You can however use the menu item File>Pre-process to generate a file where all the hash commands have been processed and the resultant code has been generated.

The Include hash command is available in SPLat/PC Build 181 and later.


# Open_Serial hash command

The Open_Serial hash command gives you a convenient way of initialising the serial port without having to manually build Communications Control Blocks (CCB) in NVEM. OPen_Serial "takes" one of a number of hash functions, each hash function being specific to a communications protocol.

The first time an Open_Serial hash command is executed in a program, the program will stall until 10 seconds from startup. It is perfectly possible to "re-open" the serial port with different settings, and the behaviour will change immediately.

Example:

# Open_Serial User(38400,8,N)

The above hash command will open the serial port at 38400, with 8 data bits and no parity, and invoke the User protocol. "User protocol" means the port can be used to send and receive any arbitrary data (as distinct from, say, ModBus, which has its own very specific data formats).

The available hash functions are listed in this section.


# Thermistor hash command

The Thermistor hash command makes it very easy to program for NTC thermistor temperature sensors.

Do not confuse thermistors with thermocouples or RTD sensors. The Thermistor hash command applies to NTC (negative temperature coefficient) thermistors, and nothing else.

Circuit configuration

The Thermistor hash command assumes the following circuit configuration, which is used in a large number of standard and custom SPLat controllers.

Example:

The following will return the temperature in ºC of a thermistor connected to analog input 0. The result will be in W. The characteristics of the thermistor and actual circuit values are defined by the hash functions that follow the Thermistor hash command.

       fAnIn    0    ;Raw thermistor voltage to W
# Thermistor Params(10000, 3840) Theta(1) Feed(5.6, 5, 50000) RangeC(-25, 50) Order(5) Display(10) ; temp now in W

The various hash functions are explained in the rest of this section.

How it works

When SPLat/PC encounters the Thermistor hash command during program translation, it uses the values supplied in the associated hash functions to calculate a table of raw analog reading versus temperature. It then processes the table to produce a best fit polynomial, and stores the polynomial coefficients in an NVEM0 table. When your SPLat program later wants to convert a raw analog thermistor reading to temperature, the raw reading is "fed into" the polynomial and converted to temperature. In effect, this process automates the older procedure given in the thermistor temperature measurement tutorial.

The result screen and sandbox

If the Thermistor hash command line includes a Display hash function, SPLat/PC will pop up a special window that shows the results of its internal calculations. This window includes a sandbox function, that lets you experiment with different values and generate a modified Thermistor command line.

Selecting a thermistor

There are no hard and fast rules for thermistor selection, but here are some pointers.

  • Thermistors are specified in terms of R25 and Beta. R25 is the resistance at 25°C. Beta is a number that defines how much the resistance changes with temperature. Typical Beta values are around 4000. You can't do much about Beta but you can usually select an R25 value.

    Many thermistor families come in a range of R25 values.Typical values are 1K, 2K, 5K, 10K, 20K, 50K and 100K.

  • The higher the temperatures you want to measure, the higher the R25 value will need to be to get optimum performance, and visa versa. For example, if you are interested in temperatures around 0°C, a 1K or 2K thermistor will be appropriate. Around room temperature a 10K unit will work well and for boiling water look at a 100K. The rule of thumb is that the thermistor resistance at the temperature of main interest should be about the same as the feed resistor Rfeed.
  • Thermistors come in many shapes and sizes. Some are designed for temperature measurement, while others are designed for other functions like circuit protection. (A common circuit protection use is inrush current limiting in power supplies). Obviously, you want one that's intended mainly for temperature sensing or measurement. You also want one you can "live with" in terms of mounting, encapsulation etc. Some companies offer thermistors already made up into probes (click here, if you are online, to search), while others sell the bare component. Betatherm is a typical company that specializes in quality thermistors for temperature measurement, but there are many others. Temperatures.com provides many more suppliers plus a lot of additional information about temperature measurement.

We are in no way associated with any of the above referenced resources and take no responsibility for their accuracy.

  • Thermistors come in many accuracy grades. Usually they are specified in terms of the R25 tolerance, with typical specs being 0.5%, 1%, 2% or sometimes 5%. As a first approximation the temperature accuracy of the thermistor at 25°C will be half the percentage tolerance, meaning a 2% thermistor will be about 1°C accurate at 25°C. That compares very well with semiconductor temperature sensors. For example, the popular LM35 has a 2.5°C accuracy for the low grade part, and 1°C for the premium part. Betatherm and several other specialist companies have thermistors with an initial accuracy of 0.05°C at 25°C

Don't equate the thermistor accuracy with the final measurement accuracy you will achieve! There are many other sources of error that cannot be controlled as easily as just paying out a few more dollars for the thermistor. The main benefit of buying a very high accuracy thermistor is interchangeability.

The thermistor hash command does not set the thermistor drive voltage. On the MMi202 and SL100 you need to initialise the thermistor drive voltage yourself.

MMi202 thermistor drive voltage SL100 thermistor drive voltage (Look near the bottom of the respective pages)


EC1 instructions

The EC1 is our first publicly available 32-bit board. As such it breaks the mould in several regards. One thing that is different is that it does not, at time of writing, support the concept of language Dialect. This section lists the instructions, directives, and hash commands/functions that are supported by the EC1 board. The list is dynamic; it will be updated to reflect the most recent firmware release for the EC1.

Other significant programming differences from old 8-bit boards are discussed below (they apply to the latest firmware version):

Enhanced MultiTrack implementation

The EC1 benefits from having much more memory in the processor chip than our classic 8-bit controllers. This has allowed us to enhance MultiTrack:

  • Each task has its own register stack (X, Y, Z, T), I, R, NVEM registers and floating point registers (U and V are still shared by all tasks). That means you can hold temporary result in registers during yielding instruction like YieldTask, Pause, WaitOn, etc. When a task is launched, its registers (except R) are set to the same values as in the task that launches it. This provides a good way of launching tasks with initial parameters. At startup the default task is deemed to be running (before any RunTasksForever).
  • Each task has its own subroutine return stack. That means you can execute yielding instruction inside subroutines, so you can now have a subroutine that performs an operation that will take some time (say heat up the boiler in a coffee machine) and only Return when the job is done.
  • MarkTime & Pause use separate timers, so they can be active at the same time.
  • Each task has its own SuperTimer scaling (more details below)
  • 64 tasks (up from 32)

PWM base frequency

The analog outputs on the EC1 are in reality pulse width modulated digital outputs. They can be filtered to produce an analog voltage. The duty cycle (percentage ON time) of the signal is proportional to the number sent to the output by an fAnOut instruction. The default frequency of the PWM signal is 40kHz. This can be changed by executing the following code:

        SetU            0,U0
        SPxCmd1         0,!CPU

where U0 is taken from the following table:

U0 | PWM frequency
0 | 16kHz
1 | 8kHz
2 | 4kHz
3 | 2kHz
4 | 1kHz
5 | 500Hz
6 | 250Hz
7 | 125Hz
8 | 50Hz (RC servo)

The 50Hz frequency is particularly suitable for driving RC (hobby) servo motors. The other frequencies give a range of choices for, for example, controlling the speed of DC motors.

Modify SuperTimer 10mS clock interval (Dialect 20 or later)

You can change the basic 10mS SuperTimer clock interval to some other value. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

The default interval is 10mS. Example, set the SuperTimer clock interval to 3mS:

         SetU            0,3
         SPxCmd1         1,!CPU

In the EC1 this change will affect only the timing of the MultiTrack task that executes it.

NVEM writes

The flash memory architecture of the processor used in the EC1 is not conducive to having a SPLatWare program writing to NVEM.

In This Section

aCom_Flags pp [D>=29]

aComDevAddr pp [D>=29]

aComRx_BufLen pp [D>=29]

aComRx_DelXChars pp [D>=29]

aComRx_fGetNum pp,fw [D>=29]

aComRx_FindXInBuf (X) [D>=23]

aComRx_Peek pp [D>=29]

aComRx_ReadOne pp [D>=29]

aComRx_StrFind pp,c [D>=28]

aComRx_StrFind2 pp,c [D>=29]

aComRx_Trim pp [D>=29]

aComSetCCB pp,nnnn [D>=29]

aComTx_Space pp [D>=29]

ADC [D>=23]

ADD [D>=23]

And

AndM

Blink oo+

Branch

BranchM mm+

ClrInstCount [D>=18]

ClrS ss+,bb* [D>=12]

ClrU

Com_Flags [D>=23]

ComDevAddr [D>=17]

Compare

ComRx_BufLen [D>=23]

ComRx_DelXChars [D>=23]

ComRx_fGetNum fw [D>=26]

ComRx_FindXInBuf (X) [D>=23]

ComRx_Peek (X) [D>=23]

ComRx_ReadOne [D>=23]

ComRx_StrFind c [D>=27]

ComRx_StrFind2 c [D>=29]

ComRx_Trim (X) [D>=23]

ComSetCCB pppp [D>=17]

ComTestStartTimer [D>=17]

ComTx_Space [D>=23]

DecI

DecM mm+

DecMGoIfNZ mm*+,LLLL

DecMRetIfNZ mm+

DecMRetIfZ mm+

DecX

defADDR Directive

defBLOCK directive

defBYTE Directive

defFLOAT Directive

defLONG Directive

defSEM Directive

defTIME24 Directive

defWORD Directive

DMGNZ mm*+,LLLL

EQU Directive

fAbs

fAdd

fAnIn c+ [D>=18]

fAnOut c+ [D>=18]

fEQU Directive

fCompareR

fDec

fDiv

fGoIfNeg LLLL

fGoIfNZ LLLL

fGoIfPos LLLL

fGoIfWGEQ LLLL [D>=23]

fGoIfWGTQ LLLL [D>=23]

fGoIfWLEQ LLLL [D>=23]

fGoIfWLTQ LLLL [D>=23]

fGoIfZ LLLL

fIEEEtoW [D>=21]

fInc

fix

FixToMem16S mm* [D>=21]

FixToMem16U mm* [D>=21]

fLoadQ ffff

fLoadW ffff

float

FloatMem16S mm* [D>=21]

FloatMem16U mm* [D>=21]

fMul

fNeg

fRecallQ mm*+

fRecallW mm*+

fSign

fStore mm*+

fSTSinceMark [D>=22]

fSTTimeSince aa+ [D>=16]

fSub

fSwap

fTest

fTestWeqQ [D>=24]

fTestWeqZ [D>=25]

fTestWgeQ [D>=25]

fTestWgeZ [D>=25]

fTestWgtQ [D>=25]

fTestWleQ [D>=25]

fTestWltQ [D>=25]

fTestWltZ [D>=25]

fTestWneQ [D>=25]

fTestWneZ [D>=25]

fWtoIEEE [D>=21]

fWtoQ

GoIfF LLLL

GoIfInK ii+,LLLL

GoIfInOff ii+,LLLL

GoIfInOn ii+,LLLL

GoIfINZ LLLL

GoIfIZ LLLL

GoIfMEQ mm+,nn,LLLL

GoIfMGE mm+,nn,LLLL

GoIfMGT mm+,nn,LLLL

GoIfMLE mm+,nn,LLLL

GoIfMLT mm+,nn,LLLL

GoIfMNE mm+,nn,LLLL

GoIfMNZ mm+,LLLL

GoIfMZ mm+,LLLL

GoIfNZ LLLL

GoIfSF ss,bb+,LLLL [D>=12]

GoIfSim LLLL

GoIfST ss,bb+,LLLL [D>=12]

GoIfT LLLL

GoIfXEQ nn,LLLL

GoIfXGE nn,LLLL

GoIfXGT nn,LLLL

GoIfXLE nn,LLLL

GoIfXLT nn,LLLL

GoIfXNE nn,LLLL

GoIfZ LLLL

GoSub LLLL

GoSubIfMNZ mm+,LLLL

GoSubIfMZ mm+,LLLL

GoSubIfNZ LLLL

GoSubIfSF ss,bb+,LLLL [D>=12]

GoSubIfST ss,bb+,LLLL [D>=12]

GoSubIfXEQ nn,LLLL

GoSubIfXGE nn,LLLL

GoSubIfXGT nn,LLLL

GoSubIfXLE nn,LLLL

GoSubIfXLT nn,LLLL

GoSubIfXNE nn,LLLL

GoSubIfZ LLLL

GoTo LLLL

IasJ: Precode [D>=16]

iEQU Directive

iiChrPrintX dest# [D>=23]

iifGetNum bb#,nn,fw [D>=28]

iifPrintWFW dest#,f,p [D>=23]

iifPrintWVW dest#,f,p [D>=23]

iiHexPrintX dest# [D>=23]

iiPrintNVText dest# [D>=27]

iiPrintText dest#,text-string [D >= 23]

iisPrintXFW dest# [D>=23]

iisPrintXVW dest# [D>=23]

iiStrFind bb#,nn,c [D>=28]

iiuPrintXFW dest# [D>=23]

iiuPrintXVW dest# [D>=23]

IncI

IncM mm+

IncX

Input ii*+

InputF ii*+

InputFM ii+

InputK ii*+

InputO ii*+

ItoX

KBeep [D>=16]

KillTask [D>=16]

LaunchTask LLLL,jj [D>=16]

LoadI nn

LoadX nn

LoopIfTiming tttttt,LLLL [D>=16]

MarkTime [D>=16]

MemToUV m,n

mEQU Directive

NoJ: Precode [D>=16]

Not

NotM

NotS ss+,bb* [D>=12]

NV0Byte Directive [D>=15]

NV0fNum Directive [D>=15]

NV0Ptr Directive [D>=15]

NV0Space Directive [D>=15]

NVAddPtr nn [D>=15]

NVAdvPtr [D>=15]

NVEM0 Directive [D>=15]

NVfGetPtrW [D>=15]

NVfPutPtrW [D>=15]

NVfReadQ nn*+ [D>=15]

NVfReadW nn*+ [D>=15]

NVIncRecNum [D>=15]

NVPopRecLen [D>=15]

NVPopRecNum [D>=15]

NVPushByte nn*+ [D>=15]

NVPushRecLen [D>=15]

NVPushRecNum [D>=15]

NVReadToPtr nn [D>=15]

NVSetPage nn [D>=15]

NVSetPtr pppp [D>=15]

NVSetRecLen nn [D>=15]

NVSetRecNum nnnn [D>=15]

NVtoUV nn,u [D>=15]

OBQC_Clr c [D>=16]

OBQC_fGet c [D>=16]

OBQC_fSet c [D>=16]

OBQC_Status c [D>=17]

oEQU Directive

Off oo*+

On oo*+

Or

OrM

Output oo*+

OutputM oo+

Pause tttt

PermRecall [D>=2]

PermStore [D>=2]

Pop

PopU n

PopV

Push

PushU n

PushV

QtoU n

Recall mm*+

RecallS ss+,bb* [D>=12]

ResetK

Resume mm*+ [D>=12/15]

RetIfF

RetIfMNZ mm+

RetIfMZ mm+

RetIfNZ

RetIfSF ss,bb+ [D>=12]

RetIfST ss,bb+ [D>=12]

RetIfT

RetIfZ

Return

RolM

RorM

RTCClrDM [D>=16]

RTCDateToUV [D>=16]

RTCDecHH [D>=25]

RTCDecMM [D>=25]

RTCDecSS [D>=25]

RTCDMToUV [D>=16]

RTCHHMMSSToUV [D>=16]

RTCHHMMToUV [D>=16]

RTCIncHH [D>=16]

RTCIncMM [D>=16]

RTCIncSS [D>=16]

RTCNotDM [D>=16]

RTCPopDM [D>=16]

RTCPopDN [D>=16]

RTCPopHH [D>=16]

RTCPopMM [D>=16]

RTCPopSS [D>=16]

RTCPushDM [D>=16]

RTCPushDN [D>=16]

RTCPushHH [D>=16]

RTCPushMM [D>=16]

RTCPushSS [D>=16]

RTCReadDate [D>=16]

RTCReadTime [D>=16]

RTCSetDM [D>=16]

RTCWriteDate [D>=16]

RTCWriteDN [D>=16]

RTCWriteTime [D>=16]

RtoX [D>=23]

RunTasks [D>=16]

RunTasksForever [D>=16]

SBC [D>=23]

sEQU Directive [D>=12]

SetMem mm*+,nn

SetS ss+,bb* [D>=12]

SetU n,i

SetV i

ShadowClear mm*+,cc

ShadowRead mm*+,cc

ShadowWrite mm*+,cc

SPxChIn cc,aaaa [In]

SPxChOut cc,aaaa [Out]

SPxCmd0 cc,aaaa [Out]

SPxCmd1 cc,aaaa [Out]

SPxCmd2 cc,aaaa [Out]

SPxCmd4 cc,aaaa [Out]

SPxPoll1 cc,aaaa [In]

SPxPoll2 cc,aaaa [In]

SPxPoll4 cc,aaaa [In]

Store mm*+

StoreS ss+,bb* [D>=12]

StringToUV n,"String"

STStart aa+ [D>=16]

STTest aa+,tttttt [D>=16]

SUB [D>=23]

Suspend mm*+ [D>=12/15]

Swap

Target LLLL

TestXeqY [D>=28]

TestXgeY [D>=28]

TestXgtY [D>=28]

TestXleY [D>=28]

TestXltY [D>=28]

TestXneY [D>=28]

UtoQ n

UtoW n

UVToMem n,mm

WaitForSF ss,bb [D>=16]

WaitForST ss,bb [D>=16]

WaitOff ii+

WaitOffT ii+,tttt

WaitOn ii+

WaitOnK ii+

WaitOnKT ii+,tttt

WaitOnT ii+,tttt

WarmBoot [D .= 20]

WtoU n

Xor

XorM

XtoI

XwireGetComErr [D>=24]

XwireGetErrCount [D>=20]

XwireGetPollCnt [D>=23]

XwireIdlePoll [D>=23]

XwireMaster pppp [D>=20]

XwirePhys nn [D>=28]

XwireSetAddr [D>=20]

XwireSlave pppp [D>=20]

XwireStop [D>=23]

YieldTask [D>=16]

# 4D_bWriteObj(ObjType, ObjIndex, aa) hash function

# 4D_fWriteObj(ObjType, ObjIndex, ff) hash function

# 4D_fWriteObjW(ObjType, ObjIndex) hash function

# 4D_NackEvent(Handler) hash function

# 4D_ObjEvent(ObjType, ObjIndex, Handler) hash function

# 4D_SetFormX() hash function

# 4D_ViSi hash command - MultiTrack+ only!

# 4D_WriteContrastX() hash function

# 4D_WriteObj(ObjType, ObjIndex, Value) hash function

# 4D_WriteObjX(ObjType, ObjIndex) hash function

# SetBGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# ButtonEvent(ID, Row, Col, Height, Width, "text"{, Mode}, ClickHandler) hash function

# ButtonParams(btn, row, col, height, width, "text", visible, ClickHandler) hash function

# Cls() hash function

# ColourWheelEvent(ev:EventHandler) hash function

# ConnectEvent(ev:EventHandler) hash function

# SetCursor(r|y:Row, c|x:Col) hash function

# CursorRel(r|y:dRow, c|x:dColumn) hash function

# Display(Bits) hash function

# Enables(Mask) hash function

# fDispW(f, p) hash function

# Feed(Vfeed, Vfs, Rfeed) hash function

# SetFGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# File(FileName) hash function

# FileOpen(FileName, Mode) hash function

# FloatVar(fArg, f, p) hash function

# GetRTC() hash function

# Hash commands and functions

# HBarOld(Row, Col, Height, FSWidth, fArg) hash function

# HexVar(bArg) hash function

# HHMMSS() hash function

# HideAllButtons() hash function

# HMI hash command

# Include hash command

# IntInputEvent(t|p:"Prompt"{, L:Min}{, h:Max}, ev:EventHandler) hash function

# ModBusMaster(BaudRate, Parity, MessageGap) hash function

# ModBusSlave(BaudRate, Parity, Address, MessageGap) hash function

# NL() hash function

# Open_Serial hash command

# Order(o) hash function

# Params(R25, Beta) hash function

# PopBGColor() hash function

# PopColors() hash function

# PopFGColor() hash function

# Port(p) hash function

# PushColors() hash function

# Print(Arg1{, Arg2 ...}) hash function

# RangeC(Tmin, Tmax) hash function

# Reset({i:BGImage}{, b:BGColour}) hash function

# SetBGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# SetColors(f:FGColor, b:BGColor) hash function

# SetCursor(r|y:Row, c|x:Col) hash function

# SetFGColor(r:Red, g:Green, b:Blue {,a:Alpha}) hash function

# SetImagePath(PathName)

# SpeakNumb(n:Number{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

# SpeakText (t:Text{, L:Language}{, c:Country}{, q:ClearQueue}) hash function

# ScreenWidthEvent(c:C, ev:EventHandler) hash function

# SPLatLink(BaudRate) hash function

# Thermistor hash command

# Theta(Rt) hash function

# uDispXFW() hash function

# uDispXVW() hash function

# User(BaudRate, Bits, Parity) hash function


Active Templates - library modules on steroids

Active Templates are an extremely powerful way of providing a library of re-usable code modules for various functions. Active Templates can embody a great deal of specialist knowledge and math. You get to use that simply by adjusting some settings.

Automatic code generation

Configure setting to suit your needs

Built in expertise. All the hard work is done for you

In brief

An Active Template is a special kind of program that runs in your PC. It is dedicated to a particular function, say temperature measurement. It has the file extension .tp1 ("tee pee one"). Once you have installed the Active Template support package you can run a template in your PC by double clicking it. When you run the template you can adjust a number of setting to adapt it to your needs. The template then automatically generates the exact SPLat code required to implement the function according to your settings. When you save the template your settings get saved inside the template file. You can also launch a template from inside SPLat/PC using the menu File>Edit Template.

What you need

To use Active Templates you need:

  • SPLat/PC 8.20.1 or later. Download
  • The Active Template support package. This allows templates to be run standalone and also contains all the runtime support files. Download
  • An active template to suit your needs. Download.

The Active Template system includes the ability to automatically check online for updates, and invite you to download newer versions of templates and the support package. You may from time to time update the support package if we write a template that requires support files that are not in existing releases.

When you download an Active Template file (*.tp1) you can simply save it alongside your SPLat program source files. The file name is used in the generated code as part of the subroutine names that you need to call to use the template. If you run a template, change its name field and save it, you will get a new tp1 file with the changed name.

Integrating into your program, overview

Active Templates are not stand-alone programs. They are software components that provide some specialized functionality. An Active Template no more generates a complete program than a timer relay is a complete control panel. To make use of a template you need to incorporate it into a larger program.

You can incorporate a template into your program by referring to it in a Builder file. Builder knows how to open a template, use your settings inside it to generate the correct SPLat code and then use the Builder tags to dovetail it into your SPLat program.

If you don't want to use Builder you can simply copy and paste the code generated by the template from the template screen directly into your program. Just follow the directions given in the comments in the generated code itself. This may seem quicker and easier initially, but it is harder to maintain. If you change one setting in the template you must rip the old code out of your program, possibly in several places, and graft in the new code. With Builder, once the .b1d file is set up it can be run any number of times on a click of your mouse.

Before trying to integrate an Active Template into your program, you need to understand what the template generated code needs to work. For example:

  • Some templates may contain one or more tasks that need to run under MultiTrack. Such templates will require a GoSub to their initialization subroutine before they will work. They will also need a RunTasksForever instruction in the startup code of your program.
  • Templates that allow your program to use Xwire peripherals will contain code to initialize Xwire and will require a GoSub to their initialization subroutine before they will work.
  • Some templates will generate and use data stored in NVEM, typically a lookup table.
  • Some templates give you a choice of how you select between several channels of something, say 4 temperature inputs. For this you need to understand the choices and pick one that suits your needs.
  • You must communicate between your code and the template code using appropriate GoSubs.

All the information on feeding and caring for a template is provided in the header comment in the generated code and in the template's documentation here in the SPLat Knowledge Base. There you will find the subroutine names to use in your GoSubs and information on how data is passed (in which registers etc).

Integrating into your program using Builder

Using Builder with templates is initially the more complicated way but it pays off in very much easier ongoing program maintenance.

  1. Read up on Builder. Without understanding Builder you will be lost.
  2. Study the documentation on the particular template you are using.
  3. Install the Active Template support package. This allows templates to be run standalone and also contains all the runtime support files. Download
  4. Place a copy of the template in the same folder as your main SPLat program.
  5. Rename it to something that suits your application. Keep it short, no more than 8 letters. Be very sure not to change the file extension. Let's say you call it mytmplt.tp1
  6. Double-click the renamed copy. If you have successfully installed the Active Template support package, the template screen will come up.
  7. Adjust the template settings to suit your needs. Make sure the Builder button is down.
  8. Click the Save button. It will be saved with the same file name but with the new settings. (if you change the Name field the template will be saved with that new name).
  9. Study the header comment on the generated code (in the code window of the template). You may find it useful to copy it into NotePad for more convenient viewing. Make most careful note of the Builder tags and subroutine names.
  10. Exit the template.
  11. Start a new SPLat file, say myprog.spt. In that file write the code that will use the template-generated code. Use Builder tags to distinguish initialization code segment, main program code segment, any NVEM0 segment(s), etc. Use segment names consistent with the template generated ones and the standard segment names documented under Builder. Save the file.
  12. Create a Builder file (you can use NotePad or the SPLat/PC editor). It should name, in appropriate order, the various segments from myprog.spt and mytmplt.tp1., then is should list those two files (in either order). Save it as, say, mybld.b1d in the same folder as your other files.
  13. Make sure you have a SPLat/PC that supports Active Templates, V8.20.1 or later. Download
  14. Close all files.
  15. Double-click the mybld.b1d file. This should launch SPLat/PC. You will be asked if you want to include trace information. Click Yes. If you have done everything correctly you will get an information box and then see the merged file in the SPLat/PC editor.
  16. Study the trace comments left by the build process. They start well over to the right of the screen.

Encapsulation/data hiding

All access from your program to the template must be via GoSub subroutine calls. Any data gets passed on the register stack (X, Y etc) or floating point registers W and/or Q. You cannot directly access any of the RAM data stored within the template generated code.

This is called encapsulation or data hiding (the two terms tend to be used interchangeably). It means that the code generated by the template is a "black box" module that can only be accessed via its "interface", i.e. the subroutines that are there to allow you access. What goes on inside is private to the module.

All other labels within the code generated by the template include a random hex number that will change every time the template is run, specifically to "hide" them from your code.

Previous Topic

Next Topic

In This Chapter

SX10500 template

RTD template


SX10500 template

The SX10500 Active Template provides easy access to a SX10500 dual thermocouple Xwire peripheral board. The board supports two K-type thermocouples and gives temperature readings from 0ºC to 1024ºC with 0.25ºC resolution. The board uses the MAX6675 chip and reflects its performance.

The template settings are:

Name box

When you Save your altered template and settings, the file produced will have this name with the extension .tp1. The name will also be used in all the program labels in the generated code, including the subroutines used to access the code generated by the template. If you change Name then all subroutine names will change.

Save button

Saves the current settings in a template file named whatever you have set in the Name box

Builder button

When depressed, the code generated will contain Builder tags. SPLat Active Templates are designed primarily to be used with Builder. Builder gives you the ability to make your programs modular, with the various module (files) only being linked together into a single SPLat program late in the process. This template optionally inserts Builder segment tags into the generated files. You should study the code in the preview window to determine which tags you must specify in your build (*.b1d) file and how to qualify them (e.g. ONCEONLY).

Preview button

Opens and closes the code preview pane

Xwire address box

Whatever you type in here will be used as the Xwire address for the board. Please study the documentation for the board to determine how to set its address and what addresses are possible. You can use a symbolic name in this box so long as elsewhere in your program you assign to it the correct numeric address via a EQU directive.

Channels

Allows you to select to support one or two channels. If you select one channel, it will correspond to the TH1 input on the SX10500 board.

Scale

Allows you to select between the result being scaled in ºC or ºF. The native mode of the measurement chip is ºC, in 0.25ºC steps, so in ºF mode you will get readings spaced 0.45ºF apart.

Access method

If you select 2 channels, you have a choice of how to extract your readings.

jndexed | Assuming you fully understand MultiTrack and the use of the J register for jndexed addressing, you can take advantage of the jndexed mode of the template. You will get one subroutine to call for both channels. The number in J determines which channel will be returned, 0 or 1. Caution: The code does not check that J contains a valid value. The number must be 0 or 1.
indexed | In indexed mode you get one subroutine to call for both channels. The number in I determines which channel will be returned, 0 or 1. Caution: The code does not check that I contains a valid value. The number must be 0 or 1.
separate subroutines | If you select this mode you will get 2 separate subroutine for retrieving readings from the two channels.

You can determine what subroutine name(s) to call from the generated code in the preview window.

Filtering

The board itself contains adjustable software noise filters on each channel. These simulate a simple RC low pass filter with a range of time constants from 1 second to 10 minutes. You can select separate filter time constants for each channel. If you check "change at runtime" you will get subroutines that let you change the time constants on the fly (at runtime). In that case your settings will be the initial values.

In brief, one time constant is the time it takes for 63% of a step change to propagate to the result.

Caution: With very long time constants you won't see the effect of temperature changes instantly. Don't mistake this for a fault.

Update checks

If the button in the top righthand corner is depressed, the template will automatically check online for any updates whenever it is run, assuming you have a live Internet connection.

Header comment

You can type whatever you like into this box. It will appear in the generated code as a comment near the top.

Using multiple SX10500 boards

If you need to use two or more SX10500 boards, simply use a separate copy of the template for each board and give them different names and addresses. The boards will have to be set to different bus addresses. There are 8 possible addresses. If you need to use more than 8 boards just contact us and we will prepare boards for you with a different set of addresses to chose from.


RTD template

The RTD Active Template lets you use Platinum Resistance Detector (RTD) temperature sensors on the standard thermistor analog inputs on MMi202 or SL100 controllers and optional SPice 10200 and SPice10211 add-on boards. It also covers the MS120 with the same SPice boards (the MS120 must have a SPice board for analog input). We advise against trying this with earlier controller models, as they did not have sufficient analog resolution for this to work.

Click here for RTD theory

The resistance of the RTD is measured using our standard thermistor configuration. The RTD and a fixed 10250 Ohm resistor on the board form a voltage divider. A programmable drive voltage up to 20V is applied to the divider and the voltage developed across the RTD is measured. This is then used to calculate the RTD resistance, and from that its temperature. This is a rough and ready method. At very low cost it will give you a reading of modest accuracy, and it can be extended to temperatures where thermistors are no very effective. An example application would be in bakers' ovens that need to operate up to 300°C with an accuracy of say 5°C.

The main limitations of this method are:

  • Sensor self-heating. The voltage/current applied to the RTD sensor are relatively high and there will be some self-heating of the sensor. The template allows for some compensation of this, if you can determine the sensor's self-heating factor, and provides an estimate of its magnitude. This is more of an issue in still-air than say in liquids.
  • Connecting lead resistance. Ideally an RTD sensor will be operated in 3-wire or 4-wire mode, where the effect of connecting wire resistance is eliminated. That is unfortunately not possible with our thermistor connection. The template lets you compensate for connecting lead resistance and displays its magnitude.
  • Resolution. Resolution is the ability to detect small changes (not to be confused with accuracy!). This method provides modest resolution rather than high resolution.
  • Electronic Accuracy. This is a measure of the difference between a resistance reading and the true value. In our circuit the main determiner is the 1% accuracy of the 10250 Ohm resistor that feeds the RTD (everything else is ratiometric). A 1% tolerance of that resistance would lead to about 2.4°C error in temperature, though in practice 1% resistors are typically much better.

The template settings are:

Name box

When you Save your altered template and settings, the file produced will have this name with the extension .tp1. The name will also be used in all the program labels in the generated code, including the subroutines used to access the code generated by the template. If you change Name then all subroutine names will change.

Save button

Saves the current settings in a template file named whatever you have set in the Name box

Builder button

When depressed, the code generated will contain Builder tags. SPLat Active Templates are designed primarily to be used with Builder. Builder gives you the ability to make your programs modular, with the various module (files) only being linked together into a single SPLat program late in the process. This template optionally inserts Builder segment tags into the generated files. You should study the code in the preview window to determine which tags you must specify in your build (*.b1d) file and how to qualify them (e.g. ONCEONLY).

Preview button

Opens and closes the code preview pane

Controller

Lets you select the controller type that you are using.

SPice

Lets you select which (if any) SPice board you are using.

Channels

As you change the controller and SPice board combination, this box will automatically list the available analog inputs in a series of check boxes. You can now select which of the available ones you want to use. When you select one, it is assigned a numeric channel number. The checkbox label is automatically set to show a channel number, a colon and a brief description of the input couched to reflect the physical markings on the actual board. For example

2: 10200/T2

means that the T2 input on the SPice10200 board has been assigned channel number 2.

Access method

If you select 2 or more channels, you have a choice of how to extract your readings.

jndexed | Assuming you fully understand MultiTrack and the use of the J register for jndexed addressing, you can take advantage of the jndexed mode of the template. You will get one subroutine to call for all channels. The number in J determines which channel will be returned, 0 or 1. Caution: The code does not check that I contains a valid value
indexed | In indexed mode you get one subroutine to call for both channels. The number in I determines which channel will be returned. Caution: The code does not check that I contains a valid value
separate subroutines | If you select this mode you will get a separate subroutine for retrieving readings from each of your selected channels.

You can determine what subroutine name(s) to call from the generated code in the preview window.

Force NoJ

Inserts NoJ: precodes on all potentially jndexed instructions. Select this option if you are calling (GoSub-ing to) the template code from jndexed MultiTrack tasks, but not using the jndexed access method. It will do little harm (simply slow things down ever so slightly) to check the box unnecessarily if you are not using jndexing.

Feed

This is the feed voltage used to drive the RTD sensor. It must be less than 20V.

R @ 0°C

This is the 0°C resistance of the RTD sensor. Common values are 100Ohm (called Pt100) and 1000Ohm (Pt1000). You should use Pt1000. Pt100 will produce poor results. Some vendors also offer Pt2000 and Pt10000. These will give better resolution at the cost of more self-heating. In a liquid, where self heating is less, that may be a workable trade-off.

Note that the template works only with IEC751 curve sensors (the most common type in America, Europe and Australasia). For all practical purposes this also covers DIN43760.

Scale

Allows you to select between the result being scaled in ºC or ºF.

Self heating

This box lets you enter the thermal resistance or self-heating coefficient of the sensor you are using, if the manufacturer provides it. Self heating is significant mainly in still air applications, and if the sensor data provides a figure it will normally be for still air conditions. The figure will then not apply to moving air or liquids.

The self heating may be specified in degrees per milliwatt or milliwatts per degree (the reciprocal). The degree scale used is generally °K or °C, which in this situation are equivalent. The template lets you enter the number in °K/mW. A typical number quoted for small sensor elements is 0.5°K/mW.

If you leave the box empty or containing zero, there will be no compensation for self heating.

Connecting cable pair

This lets you enter the length and gauge of the cable connecting the sensor to the board. You can use US units or metric units. Enter the length of the one way length, not the loop (there and back) length.

Range and estimated performance

This lets you enter the range of temperatures you are going to be measuring and gives you an estimate of two performance factors, resolution and self-heating, taking into account all the settings you have entered. You must set the top and the bottom temperature values (the middle one is optional). The template uses these to compute the optimum equation to use (a 3rd order polynomial) for converting resistance into temperature. The estimated performance figures let you explore the effect of the various settings.

Update checks

If the button in the top righthand corner is depressed, the template will automatically check online for any updates whenever it is run, assuming you have a live Internet connection.

Header comment

You can type whatever you like into this box. It will appear in the generated code as a comment near the top.


Directives

Directives are commands to SPLat/PC to do something. This contrasts with program instructions, which eventually result in an action by the SPLat controller itself. For example,

          GOTO    Foo

is a program instruction that is actioned by the controller, whereas

Bar       defByte

is a directive that causes SPLat/PC to allocate a byte of RAM to a variable named Bar.

In This Section

defADDR Directive

defBLOCK directive

defBYTE Directive

defFLOAT Directive

defLONG Directive

defSEM Directive

defTIME24 Directive

defWORD Directive

EQU Directive

fEQU Directive

iEQU Directive

NV0Byte Directive [D>=15]

NV0fNum Directive [D>=15]

NV0Ptr Directive [D>=15]

NV0Space Directive [D>=15]

NVEM0 Directive [D>=15]

mEQU Directive

oEQU Directive

sEQU Directive [D>=12]

#EQU directive


#EQU directive

The #EQU directive assigns a text string to a label.

Example

DefaultBGColour    #EQU     "128, 128, 255"

In any regular program line after this, wherever the word DefaultBGColour appears as an argument in an instruction or hash function, the string "128, 128, 255" will replace it.

Note: This is not a hash command. There must not be any spaces in the keyword.

Rules:

  1. This is not a hash command. There must not be any spaces in the keyword
  2. #EQU definitions are seen only by later lines in the program - "forward referencing" is not possible. In other words, the #EQU definition must appear before any lines that use it. For example, this will work:

    DefaultBGColour #EQU "128, 128, 255"
    .....
    #HMI SetBGColour(DefaultBGColour) Cls() ;After the definition above, will set the colour as expected

    But this will fail:

    #HMI SetBGColour(DefaultBGColour) Cls() ;BEFORE the definition below, will FAIL
    .....
     DefaultBGColour #EQU "128, 128, 255"
  3. They can be cascaded. For example this will work:

    DefaultBGColour #EQU "128, 128, 255"
    .....
    ThisScreenBGColour #EQU DefaultBGColour
  4. #EQU values cannot be redefined.
  5. #EQU values cannot be used as arguments in Formatting functions.

#EQU is particularly useful for globally changing colour schemes in display screens (e.g. SimpleHMI) and for managing memory allocations for Xwire. The following sample code shows several uses:

        Greet:          #EQU    "Hello World"

        Pi:             #EQU    3.1415926

        XWRxBlockSize   #EQU    5

        XWTxBlockSize   #EQU    2

        XWSlaveAddr     EQU     45  ;Could also be a #EQU

                    defBLOCK    XWRxBlockSize

        RxB1        defByte

        RxF1        defFLOAT

                    defBLOCK    XWTxBlockSize

        TxB1        defByte

        TxB2        defByte

        ;This will later be substituted into executable code:

                iiPrintText     251,Greet

                fLoadW          Pi

        ....

                NVEM0

        XWireCCB:   NV0Byte     XWSlaveAddr, TxB1, XWTxBlockSize, RxB1, XWRxBlockSize

a #EQU can refer to a previous #EQU, e.g.

        Hello       #EQU    "Hello World"

        Greet       #EQU    Hello

#EQU can generally be used in place of EQU and fEQU, providing the definition appears before the use. It must not be used in place of mEQU which triggers under the hood memory management operations during Translation (compilation).

See also EQU, fEQU


Precodes

Instruction precodes are special modifiers that are written immediately in front of an instruction to modify its behaviour, and which have effect only on that instruction.

Previous Topic

Next Topic

In This Section

IasJ: Precode [D>=16]

NoJ: Precode [D>=16]


Result codes

Some instructions generate a result code in the R register. This can be tested by a BranchR, so your program can react to varying outcomes.

In This Section

Result codes for CompareR

Result codes for ComRx_fGetNum

Result codes for fAdd, fSub, fMul, fDiv

Result codes for fCompareR

Result codes for fInc, fDec

Result codes for fix

Result codes for fixToU

Result codes for floatFromU

Result codes for fSetTimer

Result codes for fTest


Result codes for ComRx_fGetNum

Result (found in W) | R
Normal number | 0
NaN (Conversion failed) | 1

These results can be tested using BranchR


Result codes for fixToU

W | R
-8,388,607 to +8,388,607 | 0
W< -8,388,607; W> +8,388,607 | 1
NaN | 2

These results can be tested using BranchR


Result codes for floatFromU

U | R
-8,388,607 to +8,388,607 | 0
Else | 1

These results can be tested using BranchR


Communication Control Block elements

In This Section

Protocol

Device address

Character profile

Baud rate

Message gap x10mS

Query timeout x10mS

Query attempts


Protocol

This is a Communication Control Block (CCB) parameter. It occupies byte 0 in the CCB.

When the CCB is activated with a ComSetCCB, the specified protocol is activated and takes charge.

Protocol assignments are:

0 | SPLatLink
1 | ModBus RTU slave
2 | ModBus RTU master
3 | User programmable

Note that a protocol other than SPLatLink cannot be invoked (switched to) in the first 10 seconds after a reset.


Device address

This is a Communication Control Block (CCB) parameter. It occupies byte 1 (counting from 0) in the CCB.

This defines the controller's 8-bit address within the addressing scheme of the selected protocol (assuming the protocol supports addressing).

The controllers address can also be set programmatically by the ComDevAddr instruction. This allows for example address setting via DIP switches connected to the controller or onboard a custom controller. The most recently set address will prevail, so you should be careful about executing a fresh ComSetCCB after a ComDevAddr instruction.

Applies to the following protocols: ModBus RTU slave.


Character profile

This is a Communication Control Block (CCB) parameter. It occupies byte 2 (counting from 0) in the CCB.

Number | Data bits | Parity | Stop bits
0 | 8 | None | 1
1 | 7 | Even | 1
2 | 7 | Odd | 1
3 | 8 | Even | 1
4 | 8 | Odd | 1

Applies to the following protocols: All but SPLatLink (some profiles may be illegal with certain protocols).


Message gap x10mS

This is a Communication Control Block (CCB) parameter. It occupies byte 9 (counting from 0) in the CCB.

The ModBus RTU protocol specifies that 3.5 character times or more of silence is to be interpreted as the end of a message. It is the only mechanism specified by ModBus for the receiving device to know that a message has been completed.

In practical communications systems this can cause problems. For example, a modem can introduce random silences into the data stream as a result of its built in error detection and correction algorithms. A satellite link can introduce very long gaps, up to seconds. If you are passing data through any kind of converter boxes, gateways or routers there could also be a problem.

On transmit the SPLat will generate a minimum leadin of 3.5 character times and minimum leadout of 3.5 character times.

On receive the SPLat ModBus RTU driver users an intelligent parser that can determine when a complete message is received, without relying on a silence gap to delineate the end of a message. It is only if a message is unrecognizable that it needs to use a time limit to decide to flush out the receive buffer and start again. That time limit is the message gap.

The message gap is given in units of 10mS. If it is set to zero the SPLat will use approximately (but not less than) 3.5 character times. For non-zero values the SPLat will use the specified interval to delineate messages.

The message gap only gets invoked if a message is corrupted. That means that setting it fairly long will not slow down communications in a communications channel with a low error rate. It is best however that the Message Gap in a slave be shorter than the master's query timeout. If for some reason the master and the slave(s) get out of synchronization, the best chance for them to get back into sync is for the master to keep quite for at least the Message Gap, so the slave(s) can get a fresh start.

If you are using ModBus RTU over a simple point to point cable hook-up, there is no problem and you can set this value to zero.

Applies to the following protocols: ModBus RTU master, ModBus RTU slave.

See also: Query timeout


Query timeout x10mS

This is a Communication Control Block (CCB) parameter. It occupies byte 10 (counting from 0) in the CCB.

This time defines how long a SPLat acting as a communications master will wait for a response to a query (poll). Timing starts when the query has been fully transmitted. A complete response (i.e. the end of the response message, including its message gap) must be received within the allocated time.

This time is specified in units of 10mS. It is usually better to err on the "too long" side. Assuming most responses will be received quickly, this timeout will only expire (and consume time) if there is a problem with the communication channel.

Applies to the following protocols: ModBus RTU master.

See also: Message Gap


Query attempts

This is a Communication Control Block (CCB) parameter. It occupies byte 11 (counting from 0) in the CCB.

Sometimes a communications master will send out a query (poll, data request) but not receive a response. This can be caused by problems with the communications channel, a dead slave or other problems. This parameter specifies how many times a SPLat working as master will attempt to send a query before giving up on receiving a response.

If no response is received within the specified number of queries, an error is recorded. This can subsequently be read out using a ComErrData instruction.

Applies to the following protocols: ModBus RTU master.


ModBus Master control script commands

ModBus tutorial

ModBus script commands are stored as table entries in NVEM page 0. In the following pages, each command consists of a 1-byte numeric command code followed by 0 to several parameters. 16 bit parameters have '16' as part of their name. Other parameters are 8-bit.

Command codes less than 128 map to the same-numbered ModBus function. Codes 128 and over provide internal functions.

Previous Topic

Next Topic

In This Chapter

ReadCoil(s) - 1, SlaveAddr, CoilAddr16, ItemCount, SPAddr16

ReadInputs - 2, SlaveAddr, InAddr16, ItemCount, SPAddr16

ReadHregs - 3, SlaveAddr, RegAddr16, ItemCount, SPAddr16, SPMode

ReadInRegs - 4, SlaveAddr, InRegAddr16, ItemCount, SPAddr16

ForceCoil - 5, SlaveAddr, CoilAddr16, SPAddr16

PresetHreg - 6, SlaveAddr, RegAddr16, SPAddr16, SPMode

ForceMultCoils - 15, SlaveAddr, CoilAddr16, ItemCount, SPAddr16

PresetMultRegs - 16, SlaveAddr, RegAddr16, ItemCount, SPAddr16, SPMode

GoTo - 128, NVPtr16

Stop - 129

Delay - 130, Time


ReadCoil(s) - 1, SlaveAddr, CoilAddr16, ItemCount, SPAddr16

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 1 query (Read coil(s)), then wait for and process the response.

Parameters are:

SlaveAddr | An 8-bit slave address
CoilAddr16 | The 16-bit, 0-based address of a coil (0xxxx reference) in the slave. This will be the starting point for the read.
ItemCount | An 8-bit count of how many coils are to be read. The minimum is 1. The maximum is determined by available communications buffer size (documented elsewhere). ItemCount contiguous items will be read.
SPAddr16 | A 16-bit semaphore address within the SPLat master's RAM, counting from RAM byte 0, bit 0. For example, RAM byte 34, bit 5 would be 34x8 + 5 = 277. The ItemCount single bit items read from the slave will be stored in these semaphores.


ReadInputs - 2, SlaveAddr, InAddr16, ItemCount, SPAddr16

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 2 query (Read inputs), then wait for and process the response.

Parameters are:

SlaveAddr | An 8-bit slave address
InAddr16 | The 16-bit, 0-based address of an input (1xxxx reference) in the slave. This will be the starting point for the read.
ItemCount | An 8-bit count of how many coils are to be read. The minimum is 1. The maximum is determined by available communications buffer size (documented elsewhere). ItemCount contiguous items will be read.
SPAddr16 | A 16-bit semaphore address within the SPLat master's RAM, counting from RAM byte 0, bit 0. For example, RAM byte 34, bit 5 would be 34x8 + 5 = 277. The ItemCount single bit items read from the slave will be stored in these semaphores.


ReadHregs - 3, SlaveAddr, RegAddr16, ItemCount, SPAddr16, SPMode

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 3 query (Read holding registers), then wait for and process the response.

Parameters are:

SlaveAddr

An 8-bit slave address

RegAddr16

The 16-bit, 0-based address of a holding register (4xxxx reference) in the slave. This will be the starting point for the read.

ItemCount

An 8-bit count of how many registers are to be read. The minimum is 1. The maximum is determined by available communications buffer size (documented elsewhere). ItemCount contiguous items will be read.

SPAddr16

A 16-bit RAM address within the SPLat master's RAM. The ItemCount 16-bit registers read from the slave will be stored, starting from this address, according to the SPMode parameter.

SPMode

Determines whether data from the slave is written to SPLat RAM in byte mode (SPMode=0) or word mode (SPMode=1).

In byte mode the least significant byte from each 16-bit slave register will be written to contiguous RAM locations.

In word mode each 16-bit slave register value will be written to 2 bytes of SPLat RAM.

In either mode "bigendian" byte ordering is used. That means the first byte transmitted on the ModBus is the most significant byte (the ModBus default behaviour), and the lowest SPLat RAM location contains the most significant byte.


ReadInRegs - 4, SlaveAddr, InRegAddr16, ItemCount, SPAddr16

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 4 query (Read input registers), then wait for and process the response.

Parameters are:

SlaveAddr | An 8-bit slave address
InRegAddr16 | The 16-bit, 0-based address of an input register (1xxxx reference) in the slave. This will be the starting point for the read.
ItemCount | An 8-bit count of how many 16-bit registers are to be read. The minimum is 1. The maximum is determined by available communications buffer size (documented elsewhere). ItemCount contiguous registers will be read.
SPAddr16 | A 16-bit RAM address within the SPLat master's RAM. The ItemCount 16-bit input registers read from the slave will be stored, starting from this address. The transfer is always in word mode with the first byte of each 2-byte word sent on the ModBus stored in the lowest address in the SPLat.


ForceCoil - 5, SlaveAddr, CoilAddr16, SPAddr16

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 5 query (Force single coil), then wait for the response.

Parameters are:

SlaveAddr | An 8-bit slave address
CoilAddr16 | The 16-bit, 0-based address of a coil (0xxxx reference) in the slave.
SPAddr16 | A 16-bit semaphore address within the SPLat master's RAM, counting from RAM byte 0, bit 0. For example, RAM byte 34, bit 5 would be 34x8 + 5 = 277. The value of this semaphore will be forced into the selected coil in the slave.


PresetHreg - 6, SlaveAddr, RegAddr16, SPAddr16, SPMode

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 6 query (Force single holding register), then wait for the response.

Parameters are:

SlaveAddr

An 8-bit slave address

RegAddr16

The 16-bit, 0-based address of a holding register (4xxxx reference) in the slave.

SPAddr16

A 16-bit RAM address within the SPLat master's RAM.

SPMode

Determines whether data to the slave is transferred from SPLat RAM in byte mode (SPMode=0) or word mode (SPMode=1).

In byte mode one byte of SPLat RAM will copied to the least significant byte of the destination holding register and the most significant byte will be set to 0.

In word mode the 16-bit slave register will be written from 2 bytes of SPLat RAM.

In either mode "bigendian" byte ordering is used. That means the first byte transmitted on the ModBus is the most significant byte (the ModBus default behaviour), and the lowest SPLat RAM location contains the most significant byte.

In byte mode one byte of SPLat RAM will be sent as the second of two bytes on the ModBus, with the first byte set to 0. Thus the 16-bit register in the slave gets filled with one 8-bit value from the SPLat master.

In word mode the 16-bit slave register will be written from 2 consecutive bytes of SPLat RAM. The lowest SPLat address of each pair gets sent first. In default ModBus behaviour this will become the most significant byte of the destination register (Bigendian ordering)


ForceMultCoils - 15, SlaveAddr, CoilAddr16, ItemCount, SPAddr16

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 15 query (Force coils), then wait for the response.

Parameters are:

SlaveAddr | An 8-bit slave address
CoilAddr16 | The 16-bit, 0-based address of a coil (0xxxx reference) in the slave. This will be the starting point for the write.
ItemCount | An 8-bit count of how many coils are to be written (forced). The minimum is 1. The maximum is determined by available communications buffer size (documented elsewhere). ItemCount contiguous items will be written in the slave.
SPAddr16 | A 16-bit semaphore address within the SPLat master's RAM, counting from RAM byte 0, bit 0. For example, RAM byte 34, bit 5 would be 34x8 + 5 = 277. ItemCount semaphores read from the master, starting at this address, will be written to contiguous coils in the slave.


PresetMultRegs - 16, SlaveAddr, RegAddr16, ItemCount, SPAddr16, SPMode

A ModBus Master script command

When processed this command will result in the SPLat master sending out a ModBus function 6 query (Force multiple holding registers), then wait for the response.

Parameters are:

SlaveAddr

An 8-bit slave address

RegAddr16

The 16-bit, 0-based address of a holding register (4xxxx reference) in the slave.

ItemCount

An 8-bit count of how many 16-bit registers are to be written in the slave. The minimum is 1. The maximum is determined by available communications buffer size (documented elsewhere). ItemCount contiguous items will be written.

SPAddr16

A 16-bit RAM address within the SPLat master's RAM.

SPMode

Determines whether data to the slave is transferred from SPLat RAM in byte mode (SPMode=0) or word mode (SPMode=1).

In byte mode each byte of SPLat RAM will be sent as the second of two bytes on the ModBus, with the first byte of the pair set to 0. Thus each 16-bit register in the slave gets filled with one 8-bit value from the SPLat master.

In word mode each 16-bit slave register will be written from 2 consecutive bytes of SPLat RAM. The lowest SPLat address of each pair gets sent first. In default ModBus behaviour this will become the most significant byte of the destination register (Bigendian ordering)


GoTo - 128, NVPtr16

A ModBus Master script command. Don't confuse with the regular GoTo instruction.

When processed this command will result in a jump to the script entry at NVEM0 address NVPtr16. This is the basic mechanism for making a script run in a continuous loop.

Note that this command cannot be entered using a single NV0Byte directive, because an NVEM0 address cannot be specified in an NV0Byte list. Instead you need to use two lines. For example, if the target script line has the label Foo, the entry would be:

NV0Byte        128       ;GoTo
NV0Ptr         Foo


Stop - 129

A ModBus Master script command

When processed this command will stop further script processing. This is however done in such a way that processing can be resumed from the next script entry.


Delay - 130, Time

A ModBus Master script command

When processed this command will cause script processing to be Delayed for Time x 10mS. Time is an 8-bit number (range 0 to 255). This allows breaks of up to 2.55 seconds to be injected into ModBus processing.


SimpleHMI low level message codes

This section describes the low level serial command/message codes that flow between a SimpleHMI device and the host. Typically the SimpleHMI device is an Android phone, and the host is a SPLat controller. If you are programming a SPLat you will be using the HMI or HMI_Log hash commands and various hash functions, so all that follows is taken care of under the hood (no need to read on unless you are making SimpleHMI support for a host other than SPLat.).

See also: SimpleHMI tutorial, HMI hash command, HMI_Log hash command

General message format to SimpleHMI

All commands (a.k.a. messages, packets, datagrams) to the SimpleHMI consist of a header, a body and a terminator.

The header starts with a '01 character (this is the traditional SPLat notation for hex) followed by a command character. Command characters have been arbitrarily assigned from the visible (printing) characters, but could in future be any codes greater than '1F (invisible codes are reserved for other uses). Thus the header is always two characters.

The body consists of 0 or more arguments. Arguments are always ASCII (visible) characters, so for example the number 23 will be an ASCII 2 ('32) followed by ASCII 3 ('33). The arguments list is terminated in '03 . If there is more than 1 argument they are separated by '02 characters.

The following table summarises the complete repertoire of commands to the SimpleHMI. In this table the vertical bar (pipe) character | indicates a '02 argument separator. The '01 that starts every command, and the '03 that ends every command, are not shown.

Messages to SimpleHMI

Name

Cmd

Arguments (Body)

Description

Text

@

Text

Any following text up to the '03 terminator, is displayed at the current cursor position. No wrapping!

LogWrite

A

None

Causes an accumulated record to be written to the HMI log for the currently selected log file.

Log

B

Text

The text is added to the accumulating record for the currently selected log file.

HHMMSS

C

None

The current system time in the SimpleHMI device is added to the accumulating record for the currently selected log file.

YYMMDD

D

None

The current system time in the SimpleHMI device is added to the accumulating record for the currently selected log file.

CursorRel

E

±dRow | ±dCol

Move the text cursor relative to its current position. If an argument is empty it is unchanged. Row and column delta specifiers can be any of those defined under the CursorRel hash function.

Cursor

F

Row | Col

Move the text cursor to a new absolute position. If an argument is empty it is unchanged. Row and column specifiers can be any of those defined under the Cursor hash function.

PushColors

G

None

Push foreground and background colours onto the colour stack.

PopColors

H

None

Pop the most recently pushed colours off the colour stack and set both foreground and background colours to the retrieved values..

PopFGColor

I

None

Pop the most recently pushed colours off the colour stack and set the foreground colours to the retrieved value. The background colour is not affected.

PopBGColor

J

None

Pop the most recently pushed colours off the colour stack and set the background colours to the retrieved value. The foreground colour is not affected.

FGColorRGB

K

R | G | B values, each 0-255

Foreground colour for future text and possibly other items

BGColorRGB

L

R | G | B values, each 0-255

Background colour for future text and possibly other items, and Cls

ButtonSet

O

Btn | Row | Col | Height | Width | Text | Mode | ClickCode

Set button parameters. Coordinates in character pitch. ClickCode should not contain hex character codes less than '0E. Row and column specifiers, and Height and Width specifiers, can be any of those defined under the ButtonEvent hash function.

Mode:

In early versions this parameter was called Visible, and simply controlled visibility of the button. It now contains mode control in addition to visibility information. The coding is described in the ButtonEvent hash function.

Note: There could be some confusion between the ButtonEvent hash function, the ButtonParams hash function and the ButtonSet low level message. The ButtonEvent hash function automatically assigns a ClickCode, emits a ButtonSet message, and generates the SPLat program code to intercept the click code and call the event handler subroutine. The ButtonParams hash function also emits a ButtonSet message, to change any subset of the button's parameters. It's intended purpose is for dynamically modifying the visual attributes of the button ... you should only change the click code if you are not relying on a SPLat hash function to capture the click event.

HBar

Q

Row | Col | Height | Width | Var

Draw a filled rectangle in the position and size given using BGColor. Then overlay it with a left-aligned filled rectangle at the same coordinates, with the same height, and width Var * Width.

Requires 0 <= Var <= 1. Row and column specifiers, and Height and Width specifiers, can be any of those defined under the HBar hash function.

Cls

R

None

Clear the text, set cursor to 1,1, colour screen with the current background colour.

Reset

S

None

Reset the HMI to initial conditions

IntInput

T

Prompt | Min | Max | EventCode

Prompt user to input an integer number. Force min/max limits, return the resultant string preceded by EventCode. Return NULL if user hits Cancel. EventCode should not contain hex codes less than '0E.

HideAllButtons

U

None

Hides all the buttons and resets their parameters.

ScreenWidthEvent

V

Character_Widths | EventCode

Sets the number of characters across the screen EventCode should not contain hex codes less than '0E.

Rulers

W

None

Displays horizontal and vertical character pitch rulers. Only shows positions that are fully accessible. For example, if the selected ScreenWidthEvent value results in a character size that would fit 23.9 times into the actual screen height, the vertical ruler will contain rows zero through 22.

Enables

X

8-bit mask as hex number

Controls the visibility of user controls on the standalone version of Windows SimpleHMI (No effect in Android). The dfault are designed to suit a non-technical Enduser.

Bit 0 Enables the Connect button (Default visible)

Bit 1 Enables the Reset button (Default invisible)

Bit 2 Enables the CLS button (Default invisible)

Bit 3 Enables the Ruler button (Default invisible)

Bit 4 Enables the Aspect ratio setting (Default invisible)

Bit 5 Enables the Width setting (Default invisible)

Bit 6 Enables the Show Log button (Default invisible)

Bit 7 Enables the Terminal tab so the terminal function can be used.

FileOpen

Y

BaseName | Mode

For logging. Creates and/or switches to the file control block with the given BaseName and Mode. Subsequent logging is directed to that file.

Mode is optional, but must be specified in the first OpenFile to a particular BaseName. Default is 0. Mode should not be changed once set for a BaseName. If BaseName is omitted all logging ceases.

Mode is a 2-bit mask: Bit 0 enables Dating. Bit 1 enables file counting.

BaseName is the basis of the file name that will be used. It may contain an extension, e.g. MyFile.csv. If it does not, the default is .log

  • If dating is enabled, the name is BaseName-_YYMMDD.ext.
  • If counting is enabled, the name is BaseName_nnnnn.ext.
  • If both are enabled, the name is

BaseName-_YYMMDD_-nnnnn.ext.

BaseName must not contain -_ or _-

Mode is described in further detail under FileOpen hash function.

NewFile

Z

BaseName

Signals that a new file is to be created on the next LogWrite operation. The rules for new files depend on the Mode. Mode is described under FileOpen hash function.

ColourWheel

o

EventCode

(Android only) . Displays the colour wheel graphic. See below for what gets sent back to the host.

SpeakText

p

Text | Language | Country | ClearQueue

Speaks aloud a given text, with language and country settings. For more details of parameters see SpeakText hash function. Not implemented in Windows, where a message box pops up instead.

OldSpeakNumb

q

Text

Speaks aloud a given text, with no other parameters. Intended for speaking numbers from SPLat. With other hosts may be used as a simplified SpeakText. Not implemented in Windows, where a message box pops up instead.

Trace

!

Text

Not used in Android. Any following text up to the '03 terminator, is appended to the trace window. It can optionally be written to a disk file. '0D '0A (carriage return/line feed) gives a new line.

Messages from SimpleHMI back to the host

  • In the ButtonParams command, the last argument is called ClickCode. This defines the code that will be sent back to the host when an Enduser clicks the button. The ClickCode can be any sequence of character codes greater than '1F ('1F and below are reserved). When a button is pressed its ClickCode will be sent to the host followed by a '04.
  • In the IntInput command, the last argument is called EventCode. When the enduser completes entering the requested data, a message is sent back to the host in the following form:

    EventCode, '05, User_Response, '04

    The EventCode can be any sequence of character codes greater than '1F ('1F and below are reserved). The '05 is provided by SimpleHMI, and terminates the header. User_Response is whatever ASCII input the user provided. The '04 is the message terminator. If User_Response is empty (the '04 follows immediately after the '05), it means the user cancelled the operation without entering any data.

  • The ColourWheel command generates a stream of messages as the Enduser moves a finger across the graphic. Each message consists of:

    EventCode, '05, RedVal, GreenVal, BlueVal, '04

    There are spaces after RedVal and GreenVal.to separate the colour values. Colour values are in the range 0 to 255.

  • Other commands can result in an EventCode being sent back to the host. The EventCode can be any sequence of character codes greater than '1F ('1F and below are reserved) and will be returned by the SimpleHMI in the form:
    EventCode, '05, '04
  • When the SimpleHMI app in an Android device successfully makes a Bluetooth connection, it sends out a ConnectedEvent message. This consists of the character pair '06 '04.

These formats have been designed to work with SPLat instructions ComRx_FindXInBuf and ComRx_StrFind. ComRx_FindXInBuf will tell you if there is a '04 message terminator in the buffer. If every ClickCode and EventCode has been made unique, it is a simple matter to use ComRx_StrFind with suitable tables in NVEM0 to identify the actual event.

On other host platforms, for example a C-programmed controller, the event codes could be used to index a table of callback addresses.

Default button codes

Normally the host will tell the SimpleHMI client what click codes to send back when buttons are clicked. There are however defaults for the buttons that display as F1 through F8 on the default screen. These codes are ASCII "1" through ASCII "8" (the same as hex '31 through '38). When a button is click the code will be send followed by the '04 terminator. These codes have been designed to work with even the most simplistic of hobby grade hosts that achieve serial input by bit-banging, and block all other activity while doing so.


SPLat/PC programming software

SPLat/PC is the Windows program you use to write your SPLat program. It contains all the tools you need to write and debug SPLat programs. It has:

  • an editor for keying in your program,
  • a built in simulator that lets you thoroughly exercise your program,
  • a translator that translates it into a form the actual controller board can understand,
  • Builder, to allow you to break down your program into modules and manage inclusions and exclusions,
  • support for Active Templates, pre-packaged functions that you simply configure and incorporate into your program.
  • and functions to download the program to the controller and debug it in the controller..

SPLat/PC contains extensive documentation in its help file (totally separate from this SPLat Knowledge Base where you are now), including tutorials. You therefore have the information you need at your fingertips whenever you are using SPLat/PC. The SPLat/PC help file also contains the descriptions of the fundamental workings of SPLat, such as the registers, RAM, timers etc. That information lives in the older help file. Recently introduced and advanced topics (which tend to be the same things) will be covered here.

In May 2005 we introduced Mini Tutorials to SPLat/PC. These are short (5-10 minutes), mainly interactive tutorials that cover some aspect of SPLat usage. They are designed to provide you a quick introduction to various topics. You will find them in the SPLat/PC Help menu (SPLat/PC versions 7.15.0 or later). You can download the latest SPLat/PC here.

Previous Topic

Next Topic

In This Chapter

SPLat/PC: Onboard quadrature counter (OBQC) support

SPLat/PC: RAM allocation schemes

SPLat/PC: The preprocessor

SPLat/PC: Builder

Serial communications simulation window

Terminal (for serial comms testing)

SPLat/PC Revision history


SPLat/PC: Onboard quadrature counter (OBQC) support

SPLat/PC for dialects 16 or later (e.g. 7.16.0) supports the onboard quadrature counter with a special simulation window. You show this window with the menu selection Window>OBQC.

The window allows you to dial in a count rate up to 3kHz, and have the counter count up or down or stop. When stopped you can also type a preset number of counts into the counter (green numbers). You will be prevented from entering invalid numbers.

When the counter is counting, it does so at a simulated rate that assumes each instruction simulated by SPLat/PC is taking 50 microseconds. That is a reasonable average figure for current (2005) SPLat controllers such as SL99, MS12 and MMi20x.

The simulator is not able to recognize and quadrature count actual hardware inputs during development mode, i.e when connected to a board.


SPLat/PC: RAM allocation schemes

From version 7.15.2 (July 2005) SPLat/PC provides automatic RAM allocation. What that means is that you now have a choice between manually allocating specific RAM locations to your variables or letting SPLat/PC do it for you. Rest assured, any programs you wrote on earlier versions of SPLat/PC will work just the same as before.

Previous Topic

Next Topic

In This Section

SPLat/PC: Manual RAM allocation

SPLat/PC: Automatic RAM allocation


SPLat/PC: Manual RAM allocation

In the old (prior to July 2005) RAM allocation scheme RAM (Data Memory) was allocated to specific variables using the mEQU (or EQU) directive. For example:

Foo:    mEQU     34

would (and still will) define the label (symbol) Foo as being equivalent to the numerical value 34. This is turn means that if you subsequently use Foo as a RAM address argument in any instruction that references RAM, the value 34 will be used in its place. For example:

        IncM     Foo

will increment the contents of RAM location 34 if it exists in the same program as the previous sample line.

This is all documented in the SPLat/PC help file. Using this allocation scheme you must manually manage where in RAM each variable is to be located.

With the new automatic allocation scheme, the old EQU and mEQU directives still work. However, mEQU has been modified to work better with the new system, and now has an optional second argument. The second argument tells SPLat/PC how many bytes to reserve. RAM reserved by mEQU directives will not be used by the new automatic RAM allocation. That means you can safely mix and match the two schemes. For example, you may decide to upgrade an old program that uses the manual allocation scheme. It is perfectly OK to use automatic allocation for any new variable you need for your additional code.

The line

Bar:    mEQU     35,3

will set aside the 3 RAM locations 35, 36 and 37. These then become unavailable to the automatic allocation scheme. If the optional second argument is not included, SPLat/PC assumes 4. This ensures that if you are going to use the memory for a 4-byte floating point variable, it won't get re-allocated by the automatic allocation scheme (that would be bad!).

Note however that there is nothing to stop you overlapping allocations within the manual scheme. The following is perfectly legal:

Var1:   mEQU      27,5
Var2:   mEQU      28

You might do this for either of the following reasons:

  1. You made a mistake (the manual allocation scheme provides no protection)
  2. You intended to re-use memory. Maybe different parts of your program use it at separate times, so you can save by sharing.


SPLat/PC: Automatic RAM allocation

Automatic RAM allocation was introduced in July 2005 in SPLat/PC V7.15.2. If you have an earlier version of SPLat/PC none of the following applies. You can download the latest version of SPLat/PC from our website.

The purpose of automatic RAM allocation is to free you from having to work out exactly where in RAM to locate each and every variable in your program. It is essentially the same as the capability of programming languages optimized for computing (as distinct from control applications), which allow you to declare variables without concern about where in memory they will be located. As long as there is enough memory, they exist.

The new directives introduced for automatic RAM allocation are:

ByteVarName: defBYTE n | Allocate n bytes
WordVarName: defWORD n | Allocate n words (2 bytes per word)
AddrVarName: defADDR n | Allocate n x 2 bytes for Suspend/Resume address(es) for multitasking.
LongVarName: defLONG n | Allocate n longs (4 bytes each)
FloatVarName: defFLOAT n | Allocate n floating point variables (4 bytes each).
TimeVarName: defTIME24 n | Allocate n SuperTimers (3 bytes each) (In SPLat/PC 7.16.0 and later)
SemVarName: defSEM n | Allocate n bits for a semaphores.

For example, the program line

LoopCount:       defByte    1

will reserve one byte of RAM for a variable called LoopCount. The colon is optional. The address of that RAM byte will be remembered by SPLat/PC, and each time you use it in an instruction that accesses RAM, the numeric address will be substituted for the label LoopCount.

The line

fPressure        defFloat   3

will reserve enough RAM for 3 floating point variables called fPressure. That will mean 12 bytes are reserved (4 bytes per floating point variable). In effect this has declared a 3-element array. The individual values would be addressed using indexed or jndexed addressing.

defFLOAT and defLONG both have the same effect at present. It some future version they may be treated differently, so it is advisable to use the most appropriate one (which almost certainly means defFLOAT). Similarly for defADDR and defWORD.

Each of the above directives is detailed in the Programming Reference under Directives.

In This Section

RAM Allocation: Ground rules

Auto-Allocation: When would I use it?

Auto-Allocation: When would I not use it?

Auto-Allocation: Semaphores


RAM Allocation: Ground rules

The auto-allocation scheme is designed to co-exist with manual allocation. There are a few rules you need to remember to make the most of both schemes.

RAM used by auto-allocation

Auto-allocation will use the following RAM:

  • For normal data, anything above location 19 (inclusive) that has not been assigned in mEQU directives.
  • For semaphores, anything above location 32 (inclusive) that has not been assigned in mEQU directives.

How much RAM mEQU uses

mEQU now has an optional byte count argument. For example

Foo:      mEQU      12,8

will mark locations 12 through 19 (8 bytes) as used and give label Foo the value 12. If you omit the second argument, it is taken to be 4 (to conservatively allow space for a floating point variable).

mEQU allocations can be overlapped.

SPLat/PC will let you reference the same RAM location any number of times in mEQU directives. That means that you are totally responsible for managing the memory allocated with mEQU directives. It also allows you to allocate the same RAM to different purposes if you are quite sure they will be used at different times by your program. This can be dangerous but can conserve RAM.

You should not know where auto-allocated variables are stored

You may be able to work out where your auto-allocated variables are stored, but don't try and use that knowledge beyond very immediate debugging. There is no guarantee they won't move next time you edit your program, next time you translate it, next week or when we release a new version of SPLat/PC.


Auto-Allocation: When would I use it?

Auto-allocation is intended to make life easier for the programmer. It can be used for any "ordinary" variable storage. "Ordinary" means anything that's not to used with PermStore, not part of the "Simplified semaphore scheme" and not used for ModBus or Xwire.

Auto-allocation can be particularly useful as an aid to creating libraries of useful program functions. Each function can be stored in a file of its own, and contain all its own RAM allocations (read: "variable declarations"). When you want to use the function you simply cut and paste the whole thing into your SPLat program. Note that if you attempt this, you will need a method of assigning variable names that avoids duplication between modules.


Auto-Allocation: When would I not use it?

The times to not use auto-allocation are:

  • For permanent memory variables.
  • For data you are accessing through a communications protocol like ModBus, which assumes known, stable addresses.
  • If you need to know where the data is stored, for debugging.


Auto-Allocation: Semaphores

The auto-allocation algorithm gives special treatment to semaphores. Semaphores get auto-allocated to RAM above address 32, one bit at a time. A new byte gets allocated each time the previous one has been filled. To address auto-allocated semaphores you use only the semaphore's name (label) without a base address. SPLat/PC takes care of the base address automatically.

Example (simple semaphores with auto-allocation):

MySem0:       defSEM              ;First semaphore bit
        ...
              SetS        MySem0  ;Note: no base address
        ...
              GoIfST      MySem0,Sem0IsTrue

If you use an argument in a defSEM directive it will allocate that many contiguous bits to semaphores. This is consistent with MultiTrack and jndexing.

Example (indexed semaphores with manual allocation):

This example is how you would do it if you are using indexed addressing without MultiTrack. This technique is not recommended for new programs, because MultiTrack and jndexing, along with auto-allocation (previous example), provides a much more elegant way of doing it.

MySemArray:   defByte     6       ;Set aside 6 bytes
MySem0:       EQU         0       ;First semaphore bit
        ...
MySem7:       EQU         7       ;Last semaphore bit
        ...
              iSetS       MySem0,MySemArray

The above iSetS will set semaphore represented by bit 0 of the I'th byte in the array, where I is the index register. (The program would have to be written to be very sure I is in the range 0 to 5 or something bad might happen!)


SPLat/PC: The preprocessor

Since Build 163 SPLat/PC has contained a preprocessor. The preprocessor allows special code lines that result in significant transformation of the program prior to the actual translation process. The preprocessor is capable of things such as:

  • Including other files, to simplify libraries (re-usable code)
  • Automatic code generation for predetermined functions, such as thermistor calculations.

As an example, the preprocessor is what allowed us to make programming for SimpleHMI extremely simple. The preprocessor takes so-called hash commands and hash functions and converts them into regular SPLat code plus NVEM0 tables.

Using the preprocessor

Whenever you translate a program the preprocessor is used to process your program file before the translator itself kicks in. If you are happy to translate and immediately run your program in a SPLat board, without using the simulator (single stepping etc), then there is nothing more to do.

However, the simulator inside SPLat/PC is cannot handle hash commands. Therefore if you want to step your program you can use the menu item File>Pre-process. This will run the preprocessor and produce a new read-only file in a new editor window. That file has all the hash commands etc expanded into regular SPLat code, and can be run or single stepped in the simulator.

The best way to learn to understand the preprocessor is to copy a few of the code examples from the Hash commands and functions section of the programming reference and see what the File>Pre-process command does to them.


SPLat/PC: Builder

Builder is a new feature introduced with SPLat/PC V8.20.0. It is a project management tool that allows you to keep different parts of your project in separate source files and then merge them ("build") into a single file for final translation and download. This contrast with the older method of having your whole project in a single file. It is aimed particularly at larger projects, so if you are writing programs of less than say a couple of hundred lines, it probably is not something you need to worry about.

In brief Builder allows you to tag your source files to define different segments like constants, I/O allocations, code and NVEM page 0 data, them merge several source files into a single project, with the different segments sorted into the correct sequence. You can also control inclusions and exclusions, say to build different versions of your project for debugging versus production release.

Builder allows and encourages:

  • Modular programming whereby you write and test each sub-function of your program separately. You can leave the test code intact in the separate source files and exclude it from the final build.
  • Code re-use, so you can more easily build yourself a library of useful functions.
  • Use of Active Templates.

Builder is integrated into SPLat/PC. You can download the latest version of SPLat/PC here.

Previous Topic

Next Topic

In This Chapter

Builder: The basic idea

Builder: How you control the result, in brief

Builder: Discarding segments

Builder: Conditional segments ;<=IF

Builder: ONEONLY segment qualifier

Builder: ONCEINLINE segment qualifier

Builder: Summary of segment qualifiers

Builder: Standard segment names

Builder: Invoking it


Builder: The basic idea

A typical SPLat program will contain a number of different sections or segments:

  • A header comment at the top that describes what the program is all about.
  • A section containing various program constants, such as time limits, maximum allowable temperature, etc.
  • A section containing input and output assignments using iEQU and oEQU directives..
  • Any mEQU type absolute memory allocations (which must be before any defBYTE, defFLOAT etc.).
  • Program initialization code, for example a string of LaunchTask instructions followed by a single RunTasksForever.
  • The main body of the program. If you are a structured programmer this will consist of a number of function blocks, each handling its own well-defined area of the overall functionality (for example PumpDrive, Menu, HeaterControl).
  • A final section containing NVEM page 0 tables and/or data after a NVEM0 directive.

Now, the thing about all these sections (in Builder-speak we call them segments) is that they need to appear more or less in the order listed above. Certainly, mEQU must come before any defXXX, and NVEM0 must come at the end, after all executable code. The following diagram summarizes what Builder will do for you:

In brief, Builder allows you to work with several source files in your project. Within each source file you have named segments. Builder will take all your individual files, break them up into segments, sort them in segment order and emit a combined file where like-named segments are grouped together.


Builder: How you control the result, in brief

Builder gives you quite fine control over the way in which your final combined (or merged) file is constituted. There is some extra work involved, but for the larger projects the extra work is relatively minor and the benefits are considerable.

Builder requires some extra stuff inside each *.SPT source file, plus one *.b1d "build file". (that's a numeral one between the b and the d, to avoid conflicts with any file extension used by others).

Within the *.SPT source files you "tag" sections of code as belonging to named segments. When Builder encounters a segment tag it will direct everything below that to the named segment, until it encounters another tag. Simple tags (there are couple of slightly more complicated ones) are of the form:

;<SEGMENTNAME>	;Anything after the > will be directed to the output segment named.

The first semicolon makes this a comment line as far as the SPLat/PC simulator or translator are concerned. The word appearing between < and > is used by Builder. The < and > themselves delineate the segment name.

The leading semicolon is part of the tag. You must have ;< . A < on its own will not work.

Here is a very brief example of a source file containing 3 simple segments:

;<MEQUSEG>;Any mEQU statements go here
Counter	mEQU	12	;A counter in permanent memory
;<CODESEG>;The actual program code goes here
Count:	PermRecall
        IncM		Counter
        PermStore
        Return
;<NV0SEG>;------ NVEM0 tables --------
SineTable:	NV0fNUM	1.0
            NV0fNUM	1.02
... etc ..

The above program contains 3 segments, MEQUSEG, CODESEG and NV0SEG. The names must consist of letters and/or numerals. There is no significance to them all ending in 'SEG'. Builder does not care about upper/lower case. The names must be a single word.

The matching build file will contain 3 things:

  1. A list of the allowed segment names (you can define your own)
  2. A separator, which is #---. This just tells Builder it has come to the end of the segment names
  3. A list of the *.SPT files that are to be merged.

The build file is what controls the order in which files are read and the order in which segments are emitted. A build file for our current simple example might look like this:

MEQUSEG
CODESEG
NV0SEG
#---	;Separator
Source1.SPT
Source2.SPT
Source3.SPT

The build file may contain comments using normal SPLat conventions.

In the next few screens we will present a few additional capabilities of Builder.


Builder: Discarding segments

If you follow a segment name in the b1d file with the word DISCARD, any segments in the *.SPT source files with that name will be omitted from the build.

Example build file:

MEQUSEG
CODESEG
NV0SEG
TESTSEG DISCARD
#---	;Separator - ESSENTIAL!
Source1.SPT
Source2.SPT
Source3.SPT

This would force any test code in the individual Source files to be left out of the final build, so long as it is tagged as ;<TESTSEG>.

This is an very powerful magic, and was the original inspiration for Builder. Here's why:

Imagine you are making a large complicated program, with thousands of lines of code making up maybe 10 or 20 major modules. With the DISCARD capability you can write each module and test it on its own using built in test code. The test code allows you to test the module without being dependent on any other modules. Then, when it is time to glue all the modules together you use DISCARD to leave out the test code, but you can leave it intact in the original source file. If any changes are made to the module it can be re-tested using the original test code.

Because segment names appear as comments to the SPLat/PC translator and simulator, it is quite easy to arrange matters so a module is tested on its own, without any Build process. Being able to leave the test code in the original source file makes it easier to re-test the module if it gets altered.


Builder: Conditional segments ;<=IF

Another way of including/excluding segments from the build is called Conditional segments. Here's how it works:

Suppose in the SPLat source file you have this:

;<=IF DEBUG>	SetMem	StateNumber,6

The Builder tag is ;<=IF DEBUG> (including the semicolon!)

One of two things can happen:

  1. If the .b1d file lists DEBUG in the segment names, the final .spt file will contain
        SetMem	StateNumber,6

2. If the .b1d file does not list DEBUG in the segment names, the final .spt file will NOT contain the above SetMem instruction.

In this case the generated code line (SetMem) is not repositioned. Also, the <=IF DEBUG> tag only affects the line it appears on. Rephrasing that: A normal Builder tags acts as a switch the applies until the next Builder tag. <=IF tags only affect the current source line and do not result in the line being moved. Think of them as block tags versus line tags.

Example:

Consider the following source file, called test.spt:

;<MEQUSEG>
HourMeter	mEQU	6	;In permanent memory
;<=IF DEBUG>StateNumber	mEQU	0	;For easy readout using SPLatLink feature in SPLat/PC

;<CODESEG>

Start:

        GoSub		Initialise

;<=IF DEBUG>		SetMem	StateNumber,0

;<=IF DEBUG>		GoSub		TraceState	;Do some debug-only stuff

...

...

;<DEBUG>;---- Some debug-only code ----

TraceState:	...

            ...

        Return

Consider now the following .b1d file

;Build it with debug code

MEQUSEG

CODESEG

DEBUG

#---

test.spt

This will emit:

HourMeter	mEQU	6	;In permanent memory

StateNumber	mEQU	0	;For easy readout using SPLatLink feature in SPLat/PC

Start:

        GoSub		Initialise

        SetMem	StateNumber,0

        GoSub		TraceState	;Do some debug-only stuff

...

...

;---- Some debug-only code ----

TraceState:	...

            ...

        Return

If on the other hand I add DISCARD after the segment name DEBUG in the bld file, I will get:

HourMeter	mEQU	6	;In permanent memory

Start:

        GoSub		Initialise

...

...

In other words, all the debug code will be stripped out automatically.

Notice that the lines that are tagged with ;<=IF DEBUG> are not moved to the debug segment, they are merely switched on (but get emitted in their original positions) by the presence of DEBUG without DISCARD.

Similarly, if the segment in which a conditional segment is embedded is set to DISCARD, the conditional line(s) will also be discarded. That's what happened in the last example above.

Note that the tag ;<=IF DEBUG> will work fine if there is no mention of DEBUG in the .b1d file. The scope of this tag (range of lines it affects) is only the line it is on. The other form, ;<DEBUG> will produce an error if DEBUG is not listed in the b1d file. The scope of that form is all following lines up until the next Builder tag (or the end of the file). This is why I use DISCARD to turn the DEBUG tag on and off.

Tip: You can turn the DISCARD qualifier off by using a semicolon - it becomes a comment and is ignored, e.g.:

DEBUG ; DISCARD

That is safer than

; DEBUG


Builder: ONEONLY segment qualifier

SPLat/PC V8.20.2 and later will recognize the alias ONCEONLY

If your program uses NVEM it needs to include a NVEM0 directive to tell SPLat/PC where your executable code ends and your NVEM stuff (tables etc) starts. However a complete program may only contain one single NVEM0 directive. If you have several modules (*.spt source files), each containing NVEM, that can be a bit awkward to arrange.

Builder allows you to designate a segment name that will only be used once in the whole build process. If segment name appears in the .b1d file with the qualifier word ONEONLY after it. the segment code will be taken from the first .spt file to contain it and be ignored in any other files containing that segment. The code will be located in the output file according to the segment name's position in the build file (c.f. ONCEINLINE.

Example:

Consider 2 source files, that are listed in the .b1d in the same order as below:

1st file, test1.spt -

;<CODESEG>
;	...
;		do some stuff in test1.spt
;	...
;<NVEM0DIR>	NVEM0 ;in test1.spt
;<NVEM0DATA>
Table1:		NV0Byte  1,2,3,4,5

2nd file, test2.spt -

;<CODESEG>
;	...
;		do some stuff in test2.spt
;	...
;<NVEM0DIR>
            NVEM0 ;in test2.spt
;the above line will not get to the final file as it falls in
;segment NVEM0DIR of the 2nd .spt file to contain it.
;<NVEM0DATA>
Table2:		NV0Byte  99,100,11,22,33

And the .b1d file:

CODESEG
NVEM0DIR ONEONLY
NVEM0DATA
#---
test1.spt
test2.spt

This will build to the following merged file:

;	...

;		do some stuff in test1.spt

;	...

;	...

;		do some stuff in test2.spt

;	...

    NVEM0 ;in test1.spt

Table1:		NV0Byte  1,2,3,4,5

Table2:		NV0Byte  99,100,11,22,33

You can see that only one of the NVEM0 lines has been used.

Exercise: What will happen if I swap the test1.spt and test2.spt lines in the .b1d file? Explain why the new resulting file has one more empty line in it. It's OK to try the files to see what happens.


Builder: ONCEINLINE segment qualifier

The ONCEINLINE segment qualifier allows you to have the same bit of code in two or more source (.spt) files but only get one copy in the final result. The final code is positioned according to where it is located in the source file, not according to where the segment name is located in the build (.b1d) file. (c.f. ONEONLY)

Example:

Say you have written two modules for your library, ModA.spt and ModB.spt. Both these modules use a subroutine RotR4, which rotates the X register 4 positions to the right. You want RotR4 to be contained in both library modules because they can be used separately in some jobs but both together in others. The two files might look something like this:

1st file, ModA.spt

;<CODESEG>
ModAStfuff:
;	... do some stuff
        GoSub		RotR4
;	....  more stuff
;<ROTR4SEG>	;Here's the rotate subroutine in ModA
RotR4:	RorM
        RorM
        RorM
        RorM
        Return
;<CODESEG>
;	...  Back to more "main" module A stuff

2nd file, ModB.spt

;<CODESEG>

ModBStfuff:

;	... do some stuff

        GoSub		RotR4

;	....  more stuff

;<ROTR4SEG>	;Here's the rotate subroutine in ModB

RotR4:	RorM

        RorM

        RorM

        RorM

        Return

;<CODESEG>

;	...  Back to more "main" module B stuff

In the build file:

CODESEG

ROTR4SEG ONCEINLINE

#---

ModA.SPT

ModB.SPT

The end result after the build will be:

ModAStfuff:

;	... do some stuff

        GoSub		RotR4

;	....  more stuff

;Here's the rotate subroutine in ModA

RotR4:	RorM

        RorM

        RorM

        RorM

        Return

;	...  Back to more "main" module A stuff

ModBStfuff:

;	... do some stuff

        GoSub		RotR4

;	....  more stuff

;	...  Back to more "main" module B stuff

As you can see, only one copy of RotR4 has been included, and it is positioned as it was in the original .spt file.


Builder: Summary of segment qualifiers

In the build (.b1d) file

SegName DISCARD | The segment name is allowed in the .spt file but will not output anything
SegName ONEONLY | The source lines are emitted only from the first .spt file processed, and are positioned according to the position of the segment in the build file
SegName ONCEINLINE | The source lines are emitted only from the first .spt file processed, and are positioned according to their position in the source (.spt) file

In the source (.spt) file

;<SegName> | Code down to the next segment is emitted subject to any qualifiers in the build file. Error if the segment name is not listed in the build file.
;<=IF SegName> | The rest of this line is emitted, in the same position as in the source file, only if the segment name is listed in the build file without a DISCARD qualifier. No error (or warning!) if not in the build file. You cannot mix <=IF with ONEONLY or ONCEINLINE qualifiers. Behaviour is not guaranteed.


Builder: Standard segment names

You are free to invent your own segment names for use with Builder. The only exception is that there is one special, predefined segment called -UNAMED- which captures anything in your source files that appears before a <...> segment tag and emits them first.

In any library functions that we make we will stick as far as possible to the following list of names. It's a good idea if you use the same names for corresponding function.

DOCCOSEG | Contains any documentation on how to use a library file. DISCARD from your builds
IOSEG | Input and output assignments, including analogs
CONSTSEG | Constant values, for example time intervals
MEQUSEG | Contains any mEQU RAM allocations (say for PermStore). Must appear before any defBYTE, defFloat, defSEM etc automatic RAM allocations.
DEFSEG | Contains automatic RAM allocations defBYTE, defSEM etc. use this only if your prefer (stylistically) to have all your RAM allocations near the start of your program.
TESTSEG | Test code that should always be DISCARDed from production builds
INITSEG | Initialize code - stuff that should run once at startup. Generally calls initialize routines for various modules/objects.
LAUNCHSEG | Contains a RunTasksForever to start up MultiTrack. Should be used in .b1d files with ONEONLY
CODESEG | Actual active code. May also contain defXXX memory allocations if you prefer having them inside the module that owns them (c.f. near the top of your program.)
NVEM0DIR | Contains a NVEM0 directive. Should be used in .b1d files with ONEONLY Must appear before any other NVEM related segment.
NVEM0DATA | Contains NVEM page 0 tables or data.
XWIREINIT | Contains an instruction (typically XwireMaster XwireTab) that will start up Xwire using the configuration data located under the label XwireTab in NVEM0. Should be used in .b1d files with ONCEINLINE. Used by any of our library of Active Template modules that use Xwire.
XWIREMTAB | Contains the line label (typically XwireTab)in NVEM0 that signals the Xwire configuration table. Should be used in .b1d files with ONEONLY. Used by any of our library of Active Template modules that use Xwire.
XWIREMDATA | Contains configuration data, in NVEM0, for Xwire master operation. Used by any of our library of Active Template modules that use Xwire.
XWIREMEND | Contains an end of table sentinel for Xwire master configuration data. Should be used in .b1d files with ONEONLY. Used by any of our library of Active Template modules that use Xwire
DEBUG1 | Only ever used in <=IF tags. If enabled switches on debug features to help you debug your system.
DEBUG2 | Only ever used in <=IF tags. If enabled switches on debug features to help you debug your system.
DEBUG3 | Only ever used in <=IF tags. If enabled switches on debug features to help you debug your system.

Note that Builder will raise an error if there is a segment name in a .spt file that is not listed in the .b1d file, i.e. you must explicitly DISCARD unwanted segments. The inverse is not the case: you can have segment names in the .b1d file that do not exist in any .spt file.


Builder: Invoking it

Builder runs from within SPLat/PC V8.20.0 or later. It will only run when there are no *.spt source files or edit windows open. If you have launched SPLat/PC close down all edit windows then use the File menu. (File -> Builder)

You can also launch SPLat/PC by double clicking a .b1d file. In that case Builder will take over a build the file, leaving the result in an edit window. The built file is set to read-only. This is done to remind you that any editing of the built file will not be reflected in the original source files. You can make the built file editable by pressing CTRL+INS.


Serial communications simulation window

SPLat/PC offers two separate features related to serial communications.

  • A built in terminal emulator optimised for debugging on real SPLat boards, documented here.
  • A serial simulation feature which interacts with the simulation function in SPLat/PC. That is what this page is about.

Please be careful not to confuse the two.

The serial simulator allows you to inspect serial data transmitted out of the simulated SPLat via instructions such as iiPrintText, and to generate simulated receive data into the simulated SPLat's serial receive buffer.

You can get the serial simulation window by the menu selection Window->Serial. In its raw state it looks like this:

A small pane at the top is related to serial data sent out of the SPLat simulator. The rest is for generating receive data.

Transmitted data

Transmit data is data being transmitted out of the SPLat. From the point of view of the serial simulator window, data is being received (from the (simulated) SPLat). Thus, only the SPLat program can control what appears in the Transmitted pane.

Try the following:

  1. Launch SPLat/PC (you need version 10.23 or later. You can get the latest from our download page)
  2. Get the Serial window displayed as above (menu Window->Serial.)
  3. Set the dialect to "Anything goes" (menu File->Configure->Dialect)
  4. In the editor enter a single program line as follows:

    iiPrintText 251,"Hello World!"

    This tells the SPLat (simulator) send the text string "Hello World!" to address 251, which is the serial port. In a real board this will only work if the serial port has been set up to the User programmable protocol. The simulator is more relaxed.
  5. Single-step that instruction (F8)
  6. Look at the top part of the serial window. It should look like this:

In the top line you can see your text, with extra spacing. Under that you can see the equivalent ASCII/hexadecimal codes for the individual letters/characters. If you are sending mainly readable text, the top line is most useful. If you are sending binary codes, the second line is most useful.

To the right you will see a clear button. Just below that is a count of the number of characters sent since you last hit the Clear button. Above the Clear button is one that freezes the display. While the display is frozen the character counter still operates. Experiment with freezing the display and single stepping the one-line program. The Frz button has an associated red/green indicator.

The scroll bar is useful if there is more to display than will fit on the screen.

See what happens if you modify the SPLat instruction to add carriage return and linefeed characters ('0D and '0A), thus:

iiPrintText 251,"Hello World!",'0D,'0A

Note how non-printing characters are represented in the top line.

In the top line you can see your text, with extra spacing. Under that you can see the equivalent ASCII/hexadecimal codes for the individual letters/characters. If you are sending mainly readable text, the top line is most useful. If you are sending binary codes, the second line is most useful.

To the right you will see a clear button. Just below that is a count of the number of characters sent since you last hit the Clear button. Above the Clear button is one that freezes the display. While the display is frozen the character counter still operates. Experiment with freezing the display and single stepping the one-line program. The Frz button has an associated red/green indicator.

The scroll bar is useful if there is more to display than will fit on the screen.

See what happens if you modify the SPLat instruction to add carriage return and linefeed characters ('0D and '0A), thus:

iiPrintText 251,"Hello World!",'0D,'0A

Note how non-printing characters are represented in the top line.

Receive data

Receive data is data that in the real world will be transmitted to the SPLat and received by it. When serial data arrives at the SPLat, it gets appended to a receive buffer. The is a First In – First Out (FIFO) memory which stores received characters until the SPLat program gets around to processing them. Think of it as a character queue for incoming characters. This receive buffer has a finite capacity (typically 128 characters, depending on the SPLat model and/or Firmware version). As long as your SPLat program processes characters out of the receive buffer frequently enough to prevent it from overflowing, no receive data will be lost.

The receive pane contains 6 identical data entry windows, with associated Rx! buttons. This allows you to enter up to 6 different character sequences and simulate their reception. As most of the time you are likely to be writing a program to decode different messages, it is very useful to be able to repeat messages without constantly re-typing them.

In the low right hand corner you will see an ASCII/Hex selector. You use these to tell the simulator how you will be entering data. Select ASCII if you will be "sending" the SPLat textual data, otherwise hex.

Try this:

  1. Select ASCII
  2. In the top entry line, type "Hello SPLat"
  3. Select Hex. You will see the message you just typed change to Hex codes.
  4. To the entry line, append " 0D 0A". It should now look like this:

    48 65 6C 6C 6F 20 53 50 4C 61 74 0D 0A

What you have just done is to set up a message that contains printable characters but ends in carriage return/linefeed characters. This is quite typical of what comes out of instruments like weigh scales or micrometers (for example, our GaugeLink product, which converts the readings from a digital micrometer into serial data, produces something like that).

If you now click the Rx! button next to your message, you will see it appear in the bottom box. Click Rx! again, and a second copy will appear. You now have two copies of the same message sitting in the simulated serial receive buffer, waiting to be process by the SPLat program.

Go back to the editor window and enter the following:

ComRx_ReadOne

This instruction removes the character from the head of the serial receive buffer (the oldest character) and pushes it into the X register.

Display the SPLat registers (CTRL+R). Now single step the ComRx_ReadOne instruction. You should see two things happen:

  1. The first character in your message appears in the X register as '48 (hex format) or 72 (decimal format)
  2. The same character will have vanished from the start of the simulated receive buffer.

Each time you step the ComRx_ReadOne instruction, one character will be moved from the receive buffer to X. There are number of instructions for parsing (analyzing the contents of) the receive buffer.


Terminal (for serial comms testing)

SPLat/PC contains an integrated serial terminal which has been designed to assist in developing arbitrary communications protocols on a SPLat controller.

A little history

All SPLat controllers have a serial port. This is the port you use to connect the controller to SPLat/PC for programming. It is sometimes called the programming port. On early SPLat models we used an RS232 compatible port, and this has been carried through to recent version of the same products. Specific cases are the SL88 through SL100 family, and the MMI88 through MMI202 family (and possible descendants since this was written). On more recent models we have reduced unit costs by using a CMOS/TTL programming port, and converting it to RS232 via a programming cable (model PC232). This applies to products like CC18, MS120, HD8 and a large number of custom SPLat controllers.

However, the same serial port (sometimes called "COM port", a term popularised in the Personal Computer world) can be used for a lot of external communications during running of your application program (SPLatWare). In SPLat, we first did this by making our proprietary protocol (communications language) SPLatLink available to, for instance, allow users to have a SPLat interact with a PC, maybe for data-logging. Later we implemented ModBus, an industry standard protocol, making it possible for SPLat controllers to connect to thousands of specialised devices like variable speed drives. Most recently we have implemented "User programmable protocols". That means that it is now possible to program SPLat to a huge variety of communications protocols. A typical example of this is 3rd party products, like weigh scales, measurement instruments, motion control boards, etc., which all use their own proprietary formats for sending data or receiving commands.

While SPLatLink and ModBus are "pre-canned" protocols, "User programmable protocols" are definitely not. Developing a user protocol can be quite tricky, and some new tools were required to assist in the process.

More on SPLat serial communications and protocols.

SPLat/PC has two related but distinctly different tools for developing serial communications protocols.

  • A serial communications simulator, documented here.
  • A built in serial serial terminal, covered on this page.

Note: You need to distinguish between developing some arbitrary serial protocol and using the SimpleHMI function that is also built into SPLat/PC. In the first case you will be aiming to interact with some 3rd party product, say a weigh scale, using whatever data formats that device employs. With the SimpleHMI much of the protocol stuff is taken care of for you under the hood, and you can concentrate on your application specific requirements.

Invoking the terminal

The terminal is invoked by the button labelled "SIO" (Serial Input/Output) on the Module window (older releases of SPLat/PC called this Trm)

If you don't see that button you need to upgrade SPLat/PC. You cannot use the terminal and SPLatLink at the same time. You can only use the terminal when not in the Connected state, meaning interacting with the controller to download programs or manipulate the controller I/O.

The terminal will use the same serial port on your computer as you use for programming (COM 1 in the above screen shot). That's one of the benefits of using the SPLat/PC terminal rather than a general purpose terminal emulator like HyperTerm or SuperTerm ... there's no need to swap cables and/or ports, or to fiddle to get SPLat/PC to free the port.

When you click SIO, a new window should appear that has 3 tabs along the top, labelled Terminal, SimpleHMI and Settings. Click the Terminal tab.

The terminal looks like this:

The top black window displays serial data transmitted out of the SPLat Controller. You can chose to display in ASCII or hexadecimal. ASCII is best for human readable or "printable" material. Hex is best for binary data.

The bottom half is for data to be sent to the SPLat controller. There is room for four messages.

You can set the communication setting by clicking the Settings tab, which will expose the following setting pane:

The numbers before the colons correspond to the number used in the Communications Control Block (CCB); any combination you select here can be "reached" by a SPLat controller (and visa versa). The defaults are as shown. The current settings are shown at the bottom of the main terminal screen.

Communicating with the terminal (from your SPLat program)

You need to understand the communications (serial input and output) instructions. You also need to understand the Open_Serial hash command and its ramifications.

Viewing data transmitted out of the SPLat

Data transmitted out of the SPLat, and received by the terminal, is displayed in the top half of the terminal ("Rx"). Try running the following program with default comms settings in the terminal:

#       Open_Serial User(38400,8,N) ;Initialise the serial port to User protocol at 38.4 KBaud
        iiPrintText     251,"Hello World",'0D,'0A
        iiPrintText     251,"How are you?",'0D,'0A
A:      GoTo            A

This program uses a Hash Command to initialise the serial port. Hash commands exist in SPLat/PC Build 160 or later

When started, this will pause for 10 seconds (a forced delay before the user protocol kicks in), then send a message out of the serial port. If you have switched to the terminal before then, you should see the result in the Rx window. Click on the ASCII button so you can read it.:

You will notice that the two iiPrintText instructions both have '0D,'0A at the end. These are carriage return (CR) and line feed (LF). They force the terminal to a new line, but are otherwise invisible. Now click the HEX button. The display will switch to showing raw character codes, in hexadecimal:

Now the normally readable characters have become somewhat meaningless hex codes (unless you are an übergeek and read War and Peace in hex, for recreation), but the CR and LF have become visible. On the other hand, you could check a Hex/ASCII table and confirm the codes above.

You will discover that the only way you can connect SPLat/PC to your controller is to power cycle the controller and connect in the first 10 seconds. This is because once the "user protocol" kicks in, the controller cannot recognise a SPLat/PC command to stop. If you change the GoTo to a GoSub, you will not have to power cycle the controller to re-gain control from SPLat/PC. Why? That is an exercise for you, Gentle Reader!

Sending to the SPLat

The bottom half of the terminal window has space for four "pre-canned" messages that can be transmitted via the serial port to the SPLat. These messages are remembered between SPLat/PC sessions. Naturally, the SPLat program needs to be looking out for such messages. This is where the ComRx-something instructions come in.

The basic idea it that you enter a message and then click the corresponding Send button. You can send the same message several times.

Messages can be composed of ASCII characters and hex codes preceded by an apostrophe, and can also have comments following a single semicolon. Hence the following will send "Hello SPLat" plus CR and LF, but not ";test message"

Hello SPLat '0D '0A ;test message

If you want to send an apostrophe you can enter its hex code, '27, or two apostrophes, or an apostrophe followed by anything but a hex digit. If you want to send a semicolon you can enter its hex code, '3B, or two semi-colons. At the bottom of the Tx area is a pane that shows you, in hex, exactly what will get sent from the currently selected message box. Experiment! You won't break anything.


SPLat/PC Revision history

32-bit version

The 32-bit version of SPLat/PC provides a host of new features as well as being more stable under Windows Vista and subsequent 32-bit and 64-bit operating systems.

You can download the latest 32-bit SPLat/PC here.

Ver/date

Features/changes

4Oct10

The SPLat/PC revision history has been moved to our website here

V10.23.0

11Aug10
  • Dialect 23 support
  • Added simulator window for custom serial protocol.
  • Added simulator window for Xwire
  • Fixed various bugs and added minor enhancements

V10.21.7

15Apr10
  • Support for HD8 controller
  • Fixed incorrect reporting (wrong error line) in NVEM
  • Fixed problem in using COM ports past COM16
  • Fixed problem of simulator sometimes skipping the first instruction in a program.
  • Fixed problem of sometime missing the first line of NVEM0 data

V10.21.6

21Feb10
  • Fixed translation bug inadvertently introduced in 10.21.5 - Instructions with simple 8-bit operands (Recall, Store etc) were not being included in the translated program.

V10.21.5

19Feb10
  • NVEM compilation bug
  • Intermittent crash when switching board type
  • Improved handling of mEQU close to top of RAM
  • More accurate error reporting during symbol table build

V10.21.4

18Feb10
  • Fixed erroneous download failure message

V10.21.3

02Feb10
  • Moved housekeeping files out of "My Documents" to Application Data and Temp areas. Made handling of these files more robust, to minimize crashes if the operating system resists the automatic file and folder creation.

V10.21.2

25Jan10
  • Improved handling of "housekeeping" files written by the program, to reduce problems with running under user (non-Administrator) Windows accounts
  • Fixed bugs in CTRL+Z (undo) and CTRL+Y (redo)

V10.21.1

14JAN10
  • Program download nearly 30x faster than SPLat/PC 16-bit when used with controllers with compatible Firmware.
  • Fixed bug where the simulator fails to set or clear semaphores

V10.21.0

19DEC09
  • Dialect 21 support
  • Faster program translation (~6x)
  • Colour syntax highlighting makes it easier to spot typos
  • Easy instruction lookup. Double click on an instruction key word and press CTRL+F1 to access the instruction's help page online.
  • Split screen editing with horizontal and vertical dividers.
  • Improved printing from the editor
  • Long file names
  • Automatic online update check
  • New icon design and splash screen to differentiate from the 16-bit version.
  • New license handling

16-bit version (discontinued)

The table below lists the revision history for the 16-bit version. All recent versions of SPLat/PC have a link to online updates under the Help menu. If you are having trouble updating your SPLat/PC please contact us for assistance.

Ver/date

Features/changes

V8.20.3

04AUG08
  • Measures to minimize instability under recent Windows versions
  • Limit maximum COM port number to 9 (due to Windows bug)

(Both these are a response to Microsoft gradually breaking older applications like SPLat/PC with newer operating system versions)

V8.20.2

08JUN08
  • Added defBLOCK directive
  • Show hex object codes in .LST file

V8.20.1
  • Added active template support
  • Minor bug fixes

V8.20.0

09MAR08
  • Added Builder
  • Fixed minor bugs
  • Added support for several custom controllers

V7.20.3

19Oct07
  • Minor maintenance

V7.20.2

17Sep07
  • Fixed bug: incorrect translation of XwireGetErrCount

V7.20.1

23Aug07
  • Fixed bug: Crash with "File not found" error if never before installed on this machine.

V7.20.0
  • Dialect 20 support, notably Xwire

V7.19.5
  • Maintenance release - minor changes

V7.19.4

15Apr07
  • Generates .b1n files for use with SPDownLoad

V7.19.3

3Apr07
  • Added support for fPulse0 instruction
  • Support for certain custom boards
  • Increased symbol table space
  • Minor bug fixes

V7.19.0

9Jan07
  • Added support for MMI202, SL100, MS120 and CC16 (recent or imminent releases)
  • Dialect 19 support
  • Minor bug fixes

V7.18.4

3Oct06

Bug fix:

  • Pulse0 not flagged as illegal in dialects >15

V7.18.3

1Oct06

Bug fixes:

  • fGoIfNZ and related instructions got broken in V7.18.0 simulation
  • STScale >200 could crash simulator

V7.18.2

16Sep06
  • Previous version broke MiniTutorials.

V7.18.1

8Sep06
  • Improved analog I/O window to make it much easier to change output on controller hardware

V7.18.0

5Sep06
  • Dialect 18 support (fAnIn, fAnOut, ClrInstCount, OBLCD_HGraph)
  • Added MMi202 and SL100 (Future products)
  • New menu item Help>Examples opens the examples folder for read
  • Defaults to COM1 if not otherwise set
  • Reduced nagging about MiniTutes
  • Added new, simplified ModBus example, including VB6 program
  • Fixed minor bugs

V7.17.2

5Aug06
  • Dialect 17 support (ModBus slave RTU and communication control blocks)
  • Added full example of extended I/O using SPiceX, plus ModBus slave operation, including VB6 program (SPiceX1.VB6)

V7.16.4

17Feb06
  • Added support for TR0601 trainer board
  • Allow 15/16 bit arguments in NV0Byte list, with # prefix
  • Reduced forced mini-tute display time from 30s to 15s
  • Added OBQC_Status
  • KillTask no longer produces a warning if it is the last instruction.
  • Remove bug that sometimes caused a crash during quit.

V7.16.3

20Jan06
  • Incorporated detection of attempts to download programs translated dialect for 16 or later to boards with a dialect prior to 16, or visa versa. (Because FastTrack timing arguments have been changed from 15 bits to 24 bits in dialect 16, dialect 16 is not binary compatible with older dialects.)

V7.16.2

12Jan06
  • Tidied up showing/hiding of LCD simulator window
  • Made Antistatic mat less invasive (now defaults to off).

V7.16.1

6Jan06
  • Fixed bug whereby automatic allocation of an array of semaphores could interfere with other RAM allocations.

V7.16.0

11DEC05

First release for dialect 16 support. This is an early beta release and may contain bugs

  • MultiTrack support (simulation and code translation)
  • Mini Tutorial for MultiTrack
  • Real Time Clock support with 8 events
  • OnBoard Quadrature Counter support with simulation.

V7.15.4

04OCT05
  • Fixed bug in WaitOnT translation accidentally introduced in V7.15.3

V7.15.3

19SEP05
  • Added MMi201 support

V7.15.2

24JUL05
  • Added automatic RAM allocation.

V7.15.1

15MAY05
  • Added comprehensive simulation support for OnBoard LCD (OBLCD)
  • Extended flexibility in arguments for the OBLCD_Text instruction
  • Added "Anti-static mat" feature that overlays your desktop with a gray background to reduce visual static from desktop icons and other applications (see Window menu).

V7.15.0

04MAY05
  • Added Mini Tutorial capability. Mini Tutorials "lower the bar" virtually to ground level for people starting out with SPLat and control programming.
  • Redesigned Help menu for more emphasis on online resources
  • Added momentary action to I/O window elements (SHIFT+LeftClick)

V6.15.105APR05
  • Enhanced capability of NV0Byte directive, deprecated NV0Text.
  • Allow period (dot, full stop) character in labels

V6.15.0

03APR05
  • Added NVEM support
  • Added other dialect 15 instructions: Indexed versions of SetMem, DecMGoIfNZ, Suspend, Resume.
  • Fixed bug: Incorrect processing if an instruction contains EQU
  • Fixed bug: OBLCD_CurOn and OBLCD_CurOff transposed
  • Renamed MSCOMM.VBX to MYMSCOMM.VBX to work around a Microsoft-generated compatibility problem

V6.14.0

26Nov04
  • Added TSP support
  • All COM port selection up to 31 to accommodate USB adaptors.
  • Removed need for a password
  • Fixed various minor bugs

V6.13.2

11Jul04
  • Added support for MMi200
  • Timer window is resizable

V6.13.0

08May04
  • CTRL+H brings up the search and replace dialog in the editor
  • Initial support for dialect 13. Note: At this point dialect 13 is only available in a custom SPLat. It will be made available in standard product at a later date.
  • Support for OBCA_fRdClr. This is for the new totalizing counter mode, which is available in MMi99 firmware V3.22
    and SL99 firmware V3.41

For older version information please refer to "What's new" in the SPLat/PC help file


Tabula programming tool

Tabula is a tool that allows you to very easily specify, design and program sequential machine controls. It comes into its own in those applications where the controller must respond to external events and generate actions that depend on those events and on past history. That describes 95% of machine control applications, so chances are, if you need to program machine controls, you will find Tabula makes it extremely easy.

Sample Tabula screen. Don't worry, all shall be revealed!

Features

  • Simple table-based representation of sequential logic using Event-Action concept;
  • Automatic code generation: Reduces the amount of code you must write by up to 90%;
  • Detects common logical mistakes;
  • Supports timeouts. The number of timers is limited only by RAM. Timing code is generated automatically;
  • Full support for MultiTrack, our easy to use multitasking system;
  • Simulation, for testing your logic;
  • Supports multi-file projects using Builder, with automatic generation and versatile editing and management of Builder scripts.

YouTube movie

For a very brief overview of Tabula as a specification and design tool, see this YouTube movie. Take a few minutes to watch that before diving in.

Getting the most out of Tabula (using this documentation)

This SPLat Knowledge Base documentation has been designed as a combination tutorial and reference document. You will get the most out of it if you initially work through it from the beginning, replicating each of the steps. So click here to go to the Tabula download page and get your copy. It's free.

Tabula has been deliberately designed to make it as easy as possible to learn. For this reason some of the advanced functionality is not immediately visible when you launch Tabula. This avoids the screen clutter that might be confusing when you are first starting out.

Required skill level if you want to make simple SPLat programs

  • A need and desire to create programs for a SPLat controller that perform sequential actions triggered by external events.
  • Knowledge of a minimum of SPLat programming instructions. In fact, you can make a useful program with just 5 instructions: Input, NOT, InputK, On and Off
  • The ability to use SPLat/PC

Required skill level if you want to make sophisticated SPLat programs

  • The above, plus:
  • A willingness to knuckle down and study and learn.
  • A good knowledge of the SPLat instruction set. That doesn't mean you have to know all 450+ instructions (even inhouse we have to look up instructions!). It means you should be aware of the range of functionality provided for.
  • A proper understanding of MultiTrack.

Required skill level if you just want to design/specify logical Event-Action sequences

  • A need for a tool that will assist in designing the logic of a controller that performs sequential actions triggered by external events Tabula is a specification/design tool as well as a programming tool. It allows you to express things that can get very messy in straight-down-the-page English.
  • A willingness to spend a few hours learning a tool that could save you hours and untold grief.

Previous Topic

Next Topic

In This Chapter

Tabula: The idea of Event-Action programming

Tabula: The idea of State

Tabula: Let's generate some code! (1)

Tabula: Let's generate some code! (2)

Tabula: Let's generate some code! (3)

Tabula: Workflow for simple, single-file projects

Tabula: Making the dumb-waiter less dumb

Tabula: Timing

Tabula: The ELSE Event

Tabula: Using MultiTrack (1)

Tabula: Using MultiTrack (2)

Tabula: Using MultiTrack (3)

Tabula: The Builder window

Tabula: General code window

Tabula: Event definition window

Tabula: State definition window

Tabula: Code generation window

Tabula: Preferences menu

Tabula: Transition actions

Tabula: Backtracking

Tabula: Unreachable states

Tabula: Dead end states

Tabula: Simulation

Tabula: Editing the grid

Tabula: Self transitions and Guards

Tabula: File types

Tabula: Saving your preferences

Tabula: Capacities


Tabula: The idea of Event-Action programming

Event-Action programming is a way of viewing one of the most basic challenges in machine controls: How to describe the operation of a machine (controller) where external events trigger prescribed actions, but the proper actions depend on past history.

Most machines are equipped with a number of sensors that can detect the state of the physical apparatus (for example limit switches and operator controls) and a number of actuators that can alter the state of the apparatus (for example solenoid valves and motors). The purpose of the controller and its program is to detect events generated by the sensors and produce appropriate physical actions in the actuators.

"For every Event there is an appropriate Action. The exact action may depend on past history"

There is often a simple one-for-one correspondence between a detected event and the required action. For example, if a bottle on a conveyor passes a detector (event) the labelling sequence must be triggered (action). However, in some cases the appropriate action depends on past history ... for every 24 bottles the boxing sequence must also be triggered. The ability to modify its Event-Action behaviour is what makes a programmed controller such a powerful device. The fact that the appropriate action may depend on past history is also what makes it difficult sometimes to describe the logic of a machine control in plain "linear" English - you wind up with confusing statements along the lines of "if a-b-c happens then do so and so, unless x-y-z had happened first, in which case do a different so-and-so, but then again only if the pump is not running and has been off for at least 5 minutes with the heater on."

The above crazy statement (don't try and work it out!) is the kind of verbal/linguistic knot you can get yourself tied up in. Tabula, with the concept of Event-Action programming, resolves this, using simple tables to express sequence and past history.

The Event-Action design method expressed by Tabula is a result of very many years experience in designing control systems. It distills out common themes and patterns that occur time and again.

Event-Action programming is based on what is more commonly known as Finite State Machines (FSM). FSMs are one of the most powerful techniques in programming, and have been promoted by SPLat Controls for a long time (The very first SPLat controller, back in 1995, was designed to allow FSMs to be programmed with relative ease). However, we find that a lot of people get glazed eyes just at the mention of the term Finite State Machine. Hence, Event-Action is a way of expressing what is actually a simple concept (hopefully) even more simply.

Exercise:

  1. Pick up a push-push type ballpoint pen
    Push pen
    Describe the actions of the pen in response to pushes of the button. What are the events in this case? Does the pen have control over events?
  2. List 3 examples of where the Action of a controller in response to a given Event will depend on past history.


Tabula: The idea of State

A push pen has two states: Extended or Retracted. The pen's state depends on past events it has received. Its actions in response to new events are determined by its current state.

The idea of a state is a very powerful one, though you can't see or touch a state in the way you can often observe events and the resulting actions. The best way to give states some observable reality is perhaps to show you a Tabula table for the push pen:

The elements of this table are:

Pink cells | The lefthand column is the Event column. Each possible event is defined in a pink cell, which forms the label for its row of the table, which we sometimes call event rows.
Blue cells | Each blue cell numbers and optionally names a column. The columns represent states, so we call them state columns.
Green cells | These contain the actions that are to be performed upon entering the state (column)
White cells | The white cells (except the very top lefthand one) define the transitions (explained below). The top lefthand cell contains the name of the table.
Orange cell | This is simply a fixed label

Imagine the pen is initially retracted. It is "in" the Retracted state, which is state #1.

Now imagine the push Event takes place (i.e. you push the button). Look up the Push event row, and follow it to the column for the current state, Retracted. At the intersection of the Push event row and Retracted state column is a white cell containing "2:Extended". What this is saying is that the pen should transition to state column 2, the Extended state.

On entering state 2 the green Action cell says "Extend nib", which is what the pen does.

The pen is now "In state 2", the extended state.

With the pen in the Extended state, a push of the button will cause it to transition to state 1, Retracted, and perform the entry action "Retract nib"

Each state column defines exactly which transitions are to take place in response to individual events. The entry actions for each state define the actions to be taken on arrival in a state.

Exercise:

  1. Imagine a simple electric dumb-waiter (a small goods elevator that moves between two floors of a mansion carrying madam's lunch tray). List the possible states the dumb-waiter can be in.


Tabula: Let's generate some code! (1)

Did you do the exercise on the previous page? If not, go back and do it. You will get the most out of this if you stretch your brain a bit!

OK?

Now, you will hopefully have come up with an answer something like this:

The dumb-waiter has 4 states. They are:

  1. Downstairs (stationary)
  2. Moving up
  3. Upstairs (stationary)
  4. Moving down

Let's design the control program for this.

First of all, we will need some sensors. There will be a push button downstairs, and a push button upstairs, wired to separate controller inputs. There will also be two limit switches, one to detect when the dumb-waiter is in the downstairs position and one to detect when it is upstairs. There will be two outputs, one to drive the motor up and one to drive the motor down.

You will benefit most from this if you replicate my steps in Tabula, so launch Tabula now ...

First, let's give the table a name. Click on the top lefthand cell, the one that says [Un-named]. This will bring up a data entry screen. Type the name into the Name field.

Click OK. The name you typed now shows in the table.

Now we will name the states (blue cells). Click on blue cell #1 (which we call "the state name cell for state 1"). This will bring up another data entry screen. Type the state name "Downstairs" into the name field.

Click OK. The state name will appear in the table:

You will need to widen the column a bit to see the whole name. Position the mouse on the boundary between state 1 and state 2 name cells, until you get a double arrow

then click and drag the boundary to widen the cell.

Now enter the other 3 state names and adjust column widths, until you get this:

So far we have named the states. Let's name the Events.

Click on the top pink cell to get an event definition window, and type the event name "Up Button" into the name field

Click OK and the name will appear in the table:

Repeat the same procedure for the next 3 event name (pink) cells:

Next we will add in the transition, i.e. populate the white transition cells. You do this by considering each event in each state, and filling in any transitions that are required (I am leaving the Actions until later, but that is a personal choice).

Consider state 1, when the dumb-waiter is downstairs (and stationary). What should happen when the Up Button is pressed? That is when we want the dumb-waiter to start moving upwards. That means making a transition to state 2, "Moving up".

Click on the white cell immediately to the right of the pink Up Button cell. The cell will turn pink. Type in just the number of the state you want to transition to, i.e. '2':

... then press Enter (you can also click another cell).

What the table so far says is "When the dumb-waiter is downstairs, if the Up Button is pressed, transition to the Moving up state."

The next event of interest is going to be the "Up switch on". That will signal that the dumb-waiter has arrived upstairs.

Exercise:

  1. Complete the rest of the transitions. DO NOT go to the next screen until you have given it an honest try.


Tabula: Let's generate some code! (2)

Did you do the exercise? (If you didn't try, it's not me you are cheating!) If you are having trouble, either try re-reading from the top or re-watch my introductory YouTube movie.

Your table should look like this:

If you didn't get the same result, try and work out where you may have got confused. It is quite possible that you are trying to make this more complicated than it is. There are a whole bunch of safety interlocks etc. I may be ignoring for the sake of KISS.

At this stage you have enough effort invested in your table that it's a good idea to save it.

Call your file dumb.sfsm (sfsm stands for SPLat Finite State Machine)

At this stage let's define our physical input and outputs, i.e. what is wired to what. To keep it really simple I will just use I/O numbers directly, though that is bad practice long-term (I should be using symbolic names, but that means explaining and absorbing more data entry). Here we go:

Function | Connection on SPLat board
Up Button | Input 0
Down Button | Input 1
Upstairs limit switch | Input 2
Downstairs limit switch | Input 3
Motor Up control | Output 5
Motor Down control | Output 6

Now we are going to add some SPLat code.

Tabula requires that you add a minimal amount of actual program code, just enough to define, programmatically, Events and Actions. Think of the SPLat language as an efficient way of formally describing events and actions.

Event code

The code for an event can be any sequence of SPLat instructions that leaves a TRUE value in the X register when the Event is asserted. This is about 25% as complicated as it might sound. Here are some examples:

Instruction | Gives a TRUE result if
Input 2 | Input 2 is ON (typically the input pin on a standard SPLat input is connected to ground)
InputK 1 | Input 1 has had an ON to OFF transition. The act of testing it resets it, so this instruction is very useful for capturing button press events (was in fact designed for that)
Input 2; NOT | These two instructions will give a TRUE result if input 5 is NOT ON, i.e. OFF.
Input 2; Input 0; AND | Input 2 AND input 0 are both on

Now we have enough to define our 4 events.

Click on the first event (Up Button) to get back the Event Definition window. In the Code tab enter the instruction that will give a true if the Up button has been pressed (note that the instruction cannot start in the left margin, just like with any other SPLat programming.

Notice that I have added a comment, which will appear in the final SPLat program. This is entirely optional. Ignore the ToolTip tab.

Click OK.

If you now hover your mouse over the event cell you will get a tooltip that contains the code you just entered.

Exercise:

  1. Fill in the other 3 Event code snippets. Use Input for the limit switches and InputK for the Down button.


Tabula: Let's generate some code! (3)

Here's what you should have for the 4 events (ignoring any comments):

Event | Code snippet
Up button | InputK 0
Down Button | InputK 1
Up switch on | Input 2
Down switch on | Input 3

Make sure your table contains those snippets

Entry Action code

The process for adding code to perform actions is very similar. In this case we will be manipulating the two outputs for Motor Up and Motor Down (imagine two outputs connected two inputs on a variable speed drive).

Click on the green (entry action) cell for state 2 (not state 1 - it is usually easier to not start with state 1, which is always the initial state). You will get the State Definition window. Enter the action required upon entering the Moving up state, which is to turn the motor on in the up direction:

Similarly add the actions for entering states 2 and 3:

To enter state 2, "Upstairs", turn the motor off: Off 5.

To enter state 3, "Moving down", turn on the motor in the down direction: On 6.

Finally, the last transition, from "Moving down" to "Downstairs", turn the motor off: Off 6.

Your table should now look like this:

Time to save your file!

Note: It is legal for a state to transition to itself. In this case the entry action is not performed every time. (More on self transitions)

Now for the big moment

We are now ready to generate some output code. We have defined our events, our actions and our state transitions.

Click the menu item Code -> Generate

You will now see the code generator window, which looks like this:

I have marked the code snippets we entered manually. As you can see, those snippets have been embedded in a framework that provides the actual sequencing logic.

Exercises:

  1. Study the generated code and see if you can satisfy yourself that it will make the dumb waiter behave as designed in the Tabula table.
  2. Copy the code from the Tabula Code Generation window into the SPLat/PC editor window, and test it in the SPLat/PC simulator.
  3. Translate the code in SPLat/PC, download it to a SPLat board and run it.


Tabula: Workflow for simple, single-file projects

In the dumb-waiter example on the previous screen we manually copied and pasted the code into SPLat/PC. There is however an easier way.

For this to work you need SPLat/PC version 10.24.4 or later properly installed and registered as the default application for running *.b1d files. You can download the latest SPLat/PC here.

  • If SPLat/PC is still open, close it
  • Click on the menu selection Code->Build and Test

After a few moments SPLat/PC should launch. Dismiss any information messages you get. They are not important.

You should now have the dumb-waiter in the SPLat/PC editor, as before.

Now switch back to Tabula (don't close down SPLat/PC) and make a change, for example delete the comment after the InputK 0 in the Up Button event definition. Now switch back to SPLat/PC. Hit SHIFT+CTRL+F12 or make the following menu selection:

The modified program should appear a few moments later. If you are connected to a board this operation will even translate it, download it to the board and set it running. Now you can go from entering a change into Tabula to running in a board in a few seconds.

About Builder

Builder is a function built into SPLat/PC. It allows you to manage projects that contain multiple source (*.spt) files, merging them just before the Translation stage. Builder can, frankly, be complicated to use. In the case of Tabula we have hidden most of the complexity under the hood by incorporating automatic generation of a Builder "script". Tabula also allows you edit builder scripts and link together multiple Tabula files and other SPLat sources into a project.


Tabula: Making the dumb-waiter less dumb

There is a flaw in our dumb-waiter design:

The InputK feature of SPLat stores (latches) button presses until they are read out (tested with an InputK instruction). If a user presses a button while the system is in a state that is not testing for button presses, that button press might not be read out until much later when the program does again have in interest in the button. For example, suppose Madam, upstairs in the parlour, should press her Down button while Jeeves is downstairs in the kitchen loading the tea tray into the dumb waiter. When Jeeves pushes the up button the dumb-waiter will start its upward journey. But when it arrives upstairs (in state 4) it will see Madam's latched button press and instantly go back down to the kitchen. Madam will certainly not be impressed and poor Jeeves may fear there is a problem with his cucumber sandwiches.
The best way to counter this problem is to execute a dummy InputK as part of the the entry actions of the states affected (you are allowed any amount of code in your entry action, including GoSub). The result of the InputK instruction will be harmlessly discarded, but any pending press will be cleared.

Note that the entry action for a state is only performed once, on entry into the state.

Exercises:

  1. Test the existing program in SPLat/PC to show up the problem.
  2. Modify the entry actions where necessary to eliminate the problem. Hint: The action row can be made higher by dragging its lower boundary, much as we did to make the state columns wider, to show multiple instructions.
  3. Extend the table to provide some intelligent start-up behaviour. Determine if the dumb-waiter at power-up is upstairs or downstairs, and enter the appropriate state. If it is neither, automatically drive it downstairs. Don't be afraid to add states (you can right-click a blue state name cell and get a menu that includes Insert state).

    Test in SPLat/PC.
  4. Use your imagination, and add further refinements. Examples:
    • Door closed switches upstairs and downstairs. Prevent movement unless both are closed.
    • Solenoid operated latches to create spring-loaded automatic doors. When the dumb-waiter arrives at its destination, the door opens.


Tabula: Timing

Tabula allows for quite elaborate time related functionality.

Use the Preferences menu to Show timeout mode and Show timeout target. You will now have two new rows:

These rows give you access to powerful timing functions. They allow you to implement simple timeouts with zero coding, and more complex timing operations with minimal coding. You can have many timers going at once, you can start a timer in the entry action of any state, and you can test for timeout in the same or any other state(s).

Let's illustrate this with a simple example, a flashing LED connected to output 7

We need two states, LED On and LED off. Here's the basic table, before entering any timing stuff:

Replicate this table on your computer.

Now click on the yellow cell in state 1, and get the state definition window with the Timing tab selected:

As shown, this gives you the option to have No timeout, to Start (timing) on entry (to the state) or to Continue (timing that was started in another state). Click on "Start on entry":

You now have the option to set the time limit to a Constant or a Variable. Constant means a number that is "hardwired" into the program, say 250mS. Variable means a amount that is calculated somewhere else in the program, and may vary depending on for example a user's menu settings.

In this case we will use a constant time. Therefore we must enter a value into the text box provided. Enter the number 5 (that is only 50mS in a real SPLat board. However, if we are to test this program in the SPLat/PC simulator, which cannot generate accurate time intervals, 5 will give a noticeable period of time).

Click OK. Your table should now look like this:

The yellow cell now contains a summary of the timing mode and interval we selected. If you mouse over the cell you will get a more verbose description in a tooltip.

Do the same for state 2, but make the interval 10:

Now all that remains is to add the timeout targets in the Timeout target transition cells:

Make sure you have a name in the top lefthand cell, then save your file.

Check that there are no errors by using menu Code->Generate

Now go ahead and test your LED flasher in SPLat/PC. You should be able to simply hit CTRL+F12 in Tabula.

Exercise:

  1. Make a Tabula program that will flash an LED on for 1 second whenever an input (presumed connected to a push button) is turned on.


Tabula: The ELSE Event

Use the Preferences menu to Show ELSE row.

ELSE is an special event that is always true, so any ELSE transition will always take place (there is of course an exception, but that comes later). This may seem a bit weird, but it is actually extremely useful.

A state with an ELSE transition is a transitory one. The system cannot remain in such a state, so in theory it is not really a state at all. In fact, a state with an ELSE transition is equivalent to a decision diamond in an old fashioned flowchart or a Select Case in Visual Basic. It lets a program branch out in one of several possible ways depending on a set of conditions.

In this case it may be better to think of the "events" more as conditions, as they must be in existence upon entering the "state".

The above decision would look something like this in Tabula:


Tabula: Using MultiTrack (1)

MultiTrack is our system of multitasking in SPLat. It is simple to use, and greatly extends the range of what can be achieved with SPLat controllers. MultiTrack allows you to have up to 32 separate programs (which we call tasks) running at the same time.

If you are not familiar with MultiTrack you have two choices:

  1. Go through the tutorial introduction to MultiTrack, MultiTrack (Basic): Quick-start for Dummies and the topic after that, MultiTrack (Basic): The YieldTask instruction
  2. Give it up now as too hard, and miss out on one of the most powerful controller programming concepts on the planet.

Enable MultiTrack in Tabula with the Preferences menu. Once you have enabled Multitrack you will notice a couple of changes:

  • The General code window has become more complicated, with 6 tabs rather than one.

  • The generated code has changed, and looks more messy.

Did you get that basic understanding of MultiTrack? If you didn't, now is the time to read up on the basics. (Links above).

The purpose of multitasking (what MultiTrack provides) is to allow several "tasks" to be running at once. A Tabula "table" (technically, Finite State Machine) is generally equivalent to one task. So, with MultiTrack in a SPLat controller, you can have up to 32 separate tables, structured as MultiTrack tasks, running at once.

32 separate tables means up to 32 separate files. Hence there has to be a mechanism for combining multiple files into one project. That mechanism is called Builder, and is part of SPLat/PC. Builder needs some additional information, which is what the extra tabs in the General code window are largely about. Let's go through the most important ones (you will probably discover the uses for the others as needs arise).

CODESEG

This can contain any code that is required as part of any task specific initialisation. By convention this is a subroutine named XXX_Init, where XXX is the name you entered in the Name field of the General code window (FLSH above). Code entered into CODESEG will be placed just before the main table code.

Tabula will offer a suggestion at the bottom of the CODESEG tab. In simple cases this is all you need. The suggestion is visible only if there is nothing in the text box.

INITSEG

This can contain any code that needs to be run during the overall program initialisation. Typically it will be a GoSub to the XXX_Init subroutine described under CODESEG.

Tabula will offer a suggestion at the bottom of the INITSEG tab. In simple cases this is all you need. The suggestion is visible only if there is nothing in the text box.

LAUNCHSEG

In this tab there is a simple checkbox. It allows Tabula to automatically insert a RunTasksForever instruction into your program. RunTasksForever is necessary to get the multitasking running when the program first starts.

Exercise:

  1. Open the dumb-waiter file, save it with a new name and then convert it to MultiTrack operation. Save again after the conversion. Use the suggestions offered for CODESEG and INITSEG. Run the MultiTrack version in SPLat/PC.


Tabula: Using MultiTrack (2)

Projects with 2 or more Tabula (*.sfsm) files

You will quickly tire of having only a single task/FSM/table in your project. The real power of all this is when you have a project with a number of tasks, such as say a pump manager, a temperature control, a chemical dosing algorithm, and an operator interface, all running concurrently and interacting with each other in a properly orchestrated way.

Let's combine two of our previous examples, the dumb-waiter and the LED flasher.

The first thing to do is convert them both to MultiTrack. Open each file in turn. FIRST, save it as a new name (say DUMB_mt.sfsm and FLSH_mt.sfsm). Now enable MultiTrack in the Preferences menu, and accept the suggestions for CODESEG and INITSEG in the General code window. Save each file again.

Now, open DUMB_mt.sfsm. Select the menu item Code->Builder script. You should get a window like this:

Read the message in the bottom pane. Well, your project is NOT just the one FSM (Tabula table), so you have to do a bit more.

In the top pane you will notice a reference to the DUMB_mt.spt file. But you also need to reference FLSH_mt.spt. These are the actual SPLat source code files generated by Tabula. Make a copy of the DUMB_mt.spt line and rename it FLSH_mt.spt. You should now have:

Click the Save button. You have now modified the "Builder script" for the DUMB_mt project to include the flasher.

Make sure SPLat/PC is not open. Now the big test: Click the RUN button. A few moments later SPLat/PC will launch, and should display something like this at the top of the edit window:

If you look at this carefully you will see that you have a mash up of the two separate .spt files. This is what Builder does, so the various bits and pieces that belong together are placed together.

If all went well you can start the composite program running in the SPLat/PC simulator. Open an I/O window first, so you can interact with the program. You should see output 7 flashing while the dumb-waiter is also active.

Exercise:

  1. Create a 3rd Tabula function. Call it BLNK. What BLNK is to do is flash on output 6 for 1 time unit every time a button connected to input 4 is pressed. Merge it into the existing composite project. You should now have BUMB+FLSH+BLNK all running at the same time.


Tabula: Using MultiTrack (3)

So far we have the dumb-waiter function and the flasher function operating concurrently but in complete isolation from each other. Neither cares what the other is doing. Let's change that!

We will make it so the LED only flashes while the dumb-water is moving, so it becomes a kind of of warning signal to prevent Madam from opening the hatch at in inopportune moment and losing her tiara down the chute.

The way we will make this happen is to have DUMB maintain a semaphore signal that indicates when it is moving or not. FLSH will use that semaphore to control when it flashes an when it is off. A semaphore is a single-bit variable that can be either True or False.

Generating the sDUMB_Moving semaphore

DUMB will maintain a semaphore, which we will call sDUMB_Moving (read this as "a semaphore that is owned (controlled) by DUMB, and signals the condition Moving). Review the table for DUMB. You will observe that the dumb-waiter is moving in states 2 and 4, and stationary in states 1 and 3. Hence we want sDUMB_Moving to be set (True) in states 2 and 4 and cleared (False) in states 1 and 3. So we modify our entry actions in DUMB_mt.sfsm like this, using SetS and ClrS instructions.

You need to do one more thing in DUMB, which is to declare, or set aside memory for, sDUMB_Moving. To do that go to the Header tab in the general code editor and add a defSEM directive.

Now save DUMB_mt.sfsm

Using the sDUMB_Moving semaphore

Now we will modify FLSH_mt.sfsm.to respond to the sDUMB_Moving semaphore. This will require an new state, for when the flasher is idle.

Load FLSH_mt.sfsm. Right-click on the state 1 blue cell and select Insert State. You will get a new, empty state 1, and the two previous states will be re-numbered. Notice that the transition cells have been adjusted automatically. Name the new state Idle.

Now add two new events:

Event name | Code
Moving | RecallS sDUMB_Moving
NOT Moving | RecallS sDUMB_Moving; NOT

Add three new transition, so you wind up with this:

You can see that FLSH will now sit and wait for the sBUMB_Moving semaphore to go True (Moving), then it will flash the LED until the semaphore goes to False again (Not moving). Save the file. You did of course notice that I also reduced the on and off times to 1 timer tick (yellow cells)?.

Exercise:

  1. When the dumb-waiter stops moving, will the LED be on or off? Is this the best behaviour? If you don't think so, fix it.


Tabula: The Builder window

The Builder window lets you create, edit and manage Builder scripts, thus making it easy to manage projects that contain multiple files. You access it through the menu selection Code->Builder script.

Script edit pane

When you first generate a Tabula (*.sfsm) file, a basic Builder script (*.b1d) will be automatically generated in the script edit pane and saved alongside your *.sfsm file. For a simple, one-table project, that is all you will ever need.

However, you can also edit the script in the script edit pane, for example to add other *.spt or *.sfsm files into your project. If you add files that contain Builder segment names other than the ones initially given, you must manually edit those names into the segment list, and in the appropriate order. See Builder documentation.

Information pane

This contains some fixed, hopefully informative, text.

Script file name

This is the name of the current Builder script (*.b1d) associated with the *.sfsm file. This file name is saved with your main *.sfsm file, so by changing the name and later saving your *.sfsm file, you will create a new association. This is extremely useful for connecting a number of *.sfsm files together as a single project. Simple associate all the files in your project with the same Builder script, and you will be able to build your final project by running that Builder script from any of the places it appears, i.e.

  • In each of the Tabula files (*.sfsm)
  • In an Explorer folder - double-click the *.b1d file to launch SPLat/PC and build.

Hint: With this kind of work, it is highly advisable to configure Windows to show file extensions.

Notes:

  1. Any change to the script file name will become permanent only if you save the *.sfsm file after changing the script file name.
  2. Changes in the script file name are not included in the Edit->Undo and Edit->Redo functions of the main tabula screen, and do not trigger file save warnings.

The [...] button

Clicking this button allows you to open a different *.b1d file. Once you select a *.b1d file it will be loaded and become the one associated with the current *.sfsm file.

The Save button

This button will save the script in the Script edit pane to the currently displayed script file name. If the script file name is empty you will get a Save As dialog.

The Run button

Clicking this will save the current Builder script, and then launch it into SPLat/PC, leaving the fully built program in SPLat/PC ready to test.


Tabula: General code window

The general code window is selected from the main menu Code->Edit general code. or by clicking the top lefthand cell in the table. This window has two different appearances, depending on whether you have enabled MultiTrack in the Preferences menu. The difference is the number and names of the tabs that are displayed.

Hint: You can have the code generation window open to watch the final generated code for this *.sfsm file update dynamically as you alter the general code content.

Name field (both modes)

The name field must contain a name that is unique to this Tabula table (*.sfsm file). It is best to use a very short (2 to 4 letters) abbreviation of the function of the table. For example PMP for pump driver or HTR for heater control. This name is used in all the program labels generated by Tabula.

Startup code tab

In non-MultiTrack mode, your SPLat program will be generated as something that will run all on its own. In other words, this table is the whole program. The Startup code tab is for any code you want to appear before the table code. This may include program initialisation and directives such as EQU, mEQU, defSEM, defBYTE etc.

CODESEG tab

Any code in this segment will appear directly before the code generated by Tabula for this table. You would use it to perform initialisation that is specific to the function of this table (this state machine). It is good practice for all such code to be structured as a subroutine that gets called by a GoSub in the overall program initialisation sequence.

If there is nothing in the CODESEG code pane, Tabula will present you with a suggestion. This is simply a subroutine that launches a task XXX_Launch (where XXX is the name you entered into the Name field of the general code window).

All code files generated by Tabula will contain a label XXX_Launch. This should be used in an instruction

LaunchTask XXX_Launch

to get the table running under MultiTrack, starting in state 1.

INITSEG tab

Code in this segment will appear at the top of your program as part of the overall program initialisation sequence (after merging, perhaps, with other Tabula generated or ordinary *.spt files).

If the code pane is empty, Tabula will present a suggestion. This is simply a GoSub XXX_Init, the initialisation subroutine suggested for the CODESEG.

CAUTION: If you change the name of the table in the name field of the General code window, you will have to manually adjust the code in the CODESEG and INITSEG to reflect the change. This cannot be done automatically.

NVEM0DATA tab

NVEM (Non-Volatile MEMory) is used to store things like data tables (maybe a sine lookup table) and control blocks for Xwire and ModBus. You can enter any valid NVEM directives you wish into this pane except NVEM0. Tabula will automatically generate an NVEM0, and incorporate it into the builder script.

TESTSEG tab

TESTSEG gives you the ability to have test code embedded in your *.sfsm file. The strategy here is to test each individual Tabula file separately, and then integrate them into your overall project once you are sure each one behaves as expected. To do this you may want to write some test code (sometimes called a test harness) that assists in testing. Typically test code would use the push buttons and display of an MS120 or MMi202 as a user interface to assist in testing. The advantage of having test code as part of the file being tested is that it is easy to repeat tests if the code is every changed.

Any code entered in the TESTSEG pane will be emitted at the very top of your generated code.

Header tab

Code entered here will appear just before anything typed into CODESEG. It is intended for a "header comment", but is also a useful place to put any declarations (like defSEM, iEQU, oEQU etc.) you want to associate with the CODESEG code. Example:

************************
*   Pump manager       *
*                      *
*This state machine    *
*takes care of the pump*
************************
iPressSwitch    iEQU    8
oPump           oEQU    19

LAUNCHSEG tab

This contains a checkbox that controls if the code generator will emit the following:

;<LAUNCHSEG>;By Tabula from : XXX
        RunTasksForever

Having this in all your files does no harm. If the Builder script contains

LAUNCHSEG ONCEONLY

only one copy will appear in the final program.

Instances(J) tab

This tab lets you tell Tabula how many instances of this task you are launching. Tabula will then create that many instances of automatically declared variables. Specifically, assuming you are using Tabula's timing facility, it will generate that many defTIME24. For example, if you select 4 instances, and you are using timing, then somewhere in the generated code there will be a line

XXX_Timer:	defTIME24	4

(where XXX is the name you entered into the Name field of the general code window).


Tabula: Event definition window

The Event definition window appears when you click on a pink Event cell.

Name field

The Name field contains the name of the event. This will be the text that appears in the pink cell.

Code pane

This contains the code that will be executed by your final SPLat program to determine if the event condition is True (i.e. the event has occurred). It can contain any legal SPLat code, so long as it leaves a True or False result in the X register. For example, if the code is

Input	iButton

the event will be considered True if the iButton input is On. Note that this code does not perform a True/False test ... Tabula takes care of that and you must leave it to Tabula.

If the code pane contains something, its contents will be displayed as a tooltip when you mouse over the pink event cell.

ToolTip pane

If the code pane is empty, the content of the ToolTip pane will be displayed as a tooltip when you mouse over the pink event cell. Otherwise it is not used. When you are planning a Tabula table, you don't want to be concerned about the actual code.You can then use the ToolTip to expand on the information implied in the name (which is best kept short).


Tabula: State definition window

The state definition window appears when you click the top (blue) cell of a state column.

Name field

The Name field contains the name of the state. This will be the text that appears in the blue cell after the state number. State names are not mandatory, but can help make a table more readable.

If you use a state name with no spaces, the name will be used in the applicable line labels in the generated SPLat code. If the name has spaces, it will not be used in line labels in the generated program.

Entry action pane

This contains the code that will be executed by your final SPLat program when it enters the state. It can contain any legal SPLat code but should not do a GoTo outside itself. Normally it will be code that alters the state of outputs or internal variables.

If the code pane contains something, its contents will be displayed as a tooltip when you mouse over the pink event cell. See also Transition actions.

Comment pane

The comment is used in the tooltip that appears when you mouse over the blue state name cell or a white transition cell that points to this state.


Tabula: Code generation window

The code generation window is displayed via menu selection Code->Generate. This window can be left open and will dynamically update much of the time.

Code generator pane

The top pane contains the actual generated code. This is being constantly saved to disk with the same file name as your *.sfsm file, except with a .spt extension. For example, if the Tabula file is MyFile.sfsm, the generated SPLat file will be MyFile.spt.

Messages pane

The lower pane contains messages from the code generator. They give you good clues on how to fix up any problems.

Hint: If you keep this window open you will be able to dynamically watch out for errors. Do note however that Tabula cannot detect errors in your SPLat code, only in the logical structure of your table.


Tabula: Preferences menu

The preferences menu lets you configure a few items. These items are saved with the *.sfsm file

Show Else row

The else row specifies unconditional state transitions. An else transition will always take place, making its state transitory.

Show timeout mode, Show timeout target

The timeout mode and timeout target rows can be hidden.

MultiTrack

Lets you select whether or not to have Tabula provide MultiTrack support features and code generation.

See also Saving your preferences.


Tabula: Transition actions

The majority of Actions in a Tabula table will (and should) take place on entering states. These are the Entry Actions, and they result in the most efficient code. It is however possible to specify a separate action to take place on every transition specified in a white transition cell.

To specify such a Transition Action, right click on a cell that contains a transition. You will get a context menu that allows you to enter the action code.

The code entered here follows the same rules as entry action code. It can contain any legal SPLat code but should not do a GoTo outside itself. Normally it will be code that alters the state of outputs or internal variables.

A transition cell that contains a transition action is marked with an asterisk.

Hint: Don't make decisions in action code (i.e. perform actions depending on tests of conditions like inputs or semaphores. To do so will hide the decision making logic behind the scenes and make the table much harder to maintain. Such decisions should be shown explicitly in the visible "state logic" of the table. This, in part, is what ELSE events are for.


Tabula: Backtracking

If you right-click the blue header cell of a state, you will get a context menu that includes Backtrack:

The backtrack function gives you a list of all the states and events that transition to this state. In a large, complicated table this can be very useful.

Within the backtrack list displayed, you can click on any blue cell and backtrack the given state.


Tabula: Unreachable states

In the code generation window, the error messages may sometimes declare a state as unreachable. What this means is that there is no path between the starting state, which is always state 1, and the state in question.

Because state 1 is always the initial state, it is by definition always reachable.


Tabula: Dead end states

In the code generation window, the error messages may sometimes declare a state as a dead end. What this means is that there is no way to get out of that state once you are in it. This is usually bad.

There could be occasions when you for example want a table to run once when the system starts, then go away. You can achieve this with a dead ended state, if you are using MultiTrack. A better way is to execute a KillTask as the last instruction of the entry action of the last state, as this will free up processor resources.


Tabula: Simulation

Tabula incorporates the ability to step through an Event-Action table in a simulation mode. This is very useful for testing the soundness of your table design by playing what-if games.

If you right-click the blue header cell of a state, you will get a context menu that includes Set current state:

If you select that state header will turn red. You can now hold down the SHIFT key and mouse over the event cells. As you move over any event that is actioned in the current state, the transition cell is highlighted in yellow and the destination state is marked with a small blue marker

If you now click the mouse (still holding the SHIFT key down) Tabula will perform the transition.


Tabula: Editing the grid

The right-click menus for events (pink) and state names (blue) allow you to insert, delete, copy and paste events and states.

You can only delete unreachable states.

If you copy/paste states or events, check the transitions in the new item and make any necessary corrections.

There is a comprehensive undo/redo feature in Tabula. It saves and restores all changes to the table and associated code. It does not however cover the Builder script editing/manage


Tabula: Self transitions and Guards

Self transitions

It is OK for a state to have a transition to itself. If you do this, the entry action is not executed every time.

Guards

One possible use for a self transition is as a guard. A guard is a condition that must be met before a transition can be made out of a state. Here's how it works:

Tabula tests events in the following order:

  1. The pink events, in the order listed down the page
  2. The timeout event, if specified
  3. The ELSE event, if specified.

Imagine the first (top) event test for Input 5 being on, and transitions back to the same state. So long as input 5 is on, none of the other event checks will be performed. That means input 5 must be off before the other events can be tested and a transition to another state becomes possible. Input 5 (off) has become a guard condition.

Some experts are of the opinion that using guards can lead to bad programs. Wikipedia discussion. If you use a guard to "stall" a table before an external condition is met, you are in effect adding a new state ("waiting for input 5 to turn off") without actually having a new column in your table. It is better then to add a new state, so the intentions can be clearly read from a reading of the table.

A better way of using a guard is as an AND term that applies to all the following events. And remember, that will include timeout and ELSE.


Tabula: File types

Tabula uses the following file types:

*.sfsm | Stands for SPLat Finite State machine. This is the file that describes the structure of your table along with all the code and a number of settings such as Preferences. The format is a proprietary tagged text which is quite easy to read to facilitate text comparisons within a version control system.
*.spt | This is a SPLat source file. Tabula generates a *.spt file as its main output from the code generator.
*.b1d | Builder script file. Tabula generates a basic Builder script automatically, and allows you to edit it and extend it to serve a multiple-file project.


Tabula: Saving your preferences

The Preferences menu allows you to control the display of a few features like timing and ELSE. These setting are saved, somewhat unconventionally, in the .sfsm file. This means each file will have its own preferences.

Tabula recognizes a special file called TABULA_INI.sfsm. This file is automatically loaded when Tabula is launched from the Start menu or a shortcut icon (not if it is launched by double-clicking a *.sfsm file). TABULA_INI.sfsm serves to remember your settings in the Preferences menu plus your column width settings. If you want to change you default settings, simple set them in a blank table and save the table as TABULA_INI.sfsm.


Tabula: Capacities

Tabula has a fixed sized design grid. It will allow 100 states and 50 events.

There are 100 undo levels


Custom SPLat

The history of our company is one of supplying custom designed controllers for a wide range of Original Equipment Manufacturers. The SPLat system concept was developed against this background. SPLat is a programmable controller that exists both as a range of standard products and as a method of producing custom controller designs quickly, inexpensively and above all, with zero risk.

Previous Topic

Next Topic

In This Chapter

Custom SPLat: How it gives you an edge

Custom SPLat: The development process

Custom SPLat: Add some SPice

SPLat Circuit Element Library


Custom SPLat: How it gives you an edge

A custom SPLat controller can give your product a huge advantage compared to other embedded control solutions. A custom SPLat can be designed with the exact mix of features you need for your product. This may be special function inputs and outputs, a physical form that suits your product or even some Firmware functions that harness the power of the SPLat processor to your unique needs.

SPLat offers you many opportunities to increase the performance and customer value of your product. Consider such things as a better user interface than you could otherwise afford, a more integrated control system (i.e. fewer bits to inventory) or something as mundane as simplified wiring. With competing solutions some incremental facilities could be totally unaffordable, whereas with SPLat you can afford to add some extra sensing, or smarter sensing.

The other kind of scenario is where you need an electronic feature that simply cannot be bought off the shelf. Examples of this would be:

  • Driving a proportional valve that needs 0-150mA constant current (we've done that!)
  • Reading a proprietary encoder that has an RS485 interface with an unpublished format (we cracked that one, and saved our customer's business in the process).
  • Making an analog input circuit with a very, very high input impedance to read a very specialized sensor element. (Our customer didn't even know there was an issue before we solved it).
  • Interfacing a temperature/humidity sensor that has an output signal that complies with no standard whatsoever. (Sensirion make super sensors, but unless you have access to electronics design skills you are out of luck).

These kind of things are show stoppers unless you can get some custom circuitry included in your controller.

This section of the SPLat Knowledge Base gives you an overview of what is entailed in having a custom SPLat. It shows the comparative costs, both for design and production, compared to other solutions. It also compares the risk factors, especially compared to a full "from the ground up" custom controller design. We will describe the actual development process, both in term of what you need to contribute and how we will help you.

Previous Topic

Next Topic

In This Chapter

Custom SPLat: What makes SPLat unique?

Custom SPLat: Engineering Costs (NRE)

Custom SPLat: Production costs


Custom SPLat: What makes SPLat unique?

SPLat blurs the distinction (chasm?) between off the shelf controls and custom controls. Before SPLat you had 3 broad choices for controls in a product: Off the shelf PLCs, off the shelf single board computer (SBC) products or a full custom (usually microprocessor based) embedded system controller.

SPLat combines the best of each, plus some fresh new ideas, to give you a spread of solutions that give you a smooth path all the way from low cost, easy to use off the shelf products for early stage work through to custom SPLat controllers for volume manufacture.

One of the keys to understanding SPLat, and why it costs less than alternative solutions, is to realise that we are very big on finding solutions that are appropriate and not over designed. For example, we incorporate temperature sensing in a lot of our designs, using a low cost thermistor technique. This is not a precision system. In fact, it has limited accuracy and resolution. But if you are making an industrial cleaning system do you think it really matters if the water is at 84°C or 86°C? So would you rather spend $10 for 1°C accuracy and 0.5°C resolution, or $200+ for 0.1°C performance? By the way, we can do the other as well. We make controllers for autoclaves that PID control to ±0.1°C


Custom SPLat: Engineering Costs (NRE)

The graph above compares the NRE costs of various embedded system solutions. It covers the three traditional solutions and 3 points on the continuum of SPLat solutions.

Full Custom

For very large volume applications, a full custom microcontroller based embedded system will undoubtedly give the lowest unit costs. However, the NRE is very high, and there is also a significant risk factor.

Single Board Computer

Conventional single board computers eliminate a lot of the risk, at least on the hardware side. Nevertheless, programming can be very difficult, and expensive, unless you happen to have considerable skills in C or assembler. You may also have to engineer special function interface boards.

PLC

Using traditional programmable logic controllers can be comfortable, especially if you (or your automation consultant) have a background in electrical or plant automation. Beware, though, that with a traditional programmable logic controller programming cost can rise very rapidly with increasing functional complexity. And as soon as you need a specialised input or output, like a temperature measuring input, you are facing huge prices for industrial modules.

Standard SPLat

The NRE with standard SPLat is low because unlike single board computers a SPLat is ready to use 'out of the box', and programming is much simpler than both SBCs and traditional programmable logic controllers (the latter being especially true if the programmer has no prior exposure to PLC programming).

SPLat with SPice

Our Touch of SPice concept allows you to add low cost, special function add-on boards to a standard SPLat in order to tailor it to your specific needs. Not only do we have a range of ready-made SPice add-ons, but we can also make you a SPice board to suit your specific needs quickly and at a very affordable price in quantities down to 15.

Custom SPLat

The NRE for a custom SPLat is much, much lower than for a full custom system, because we design to a recipe (including SPice!). Simply stated, we have eliminated the uncertainties and we have some of the most experienced embedded systems designers on the planet.


Custom SPLat: Production costs

In terms of production costs, if amortised NRE is taken into account, the only solution that can compete with SPLat is a full custom embedded system, and then only at very high volumes where the NRE has been diluted into insignificance.

PLCs

Conventional programable logic controller solutions are generally dearer than SPLat. This is not so much because you can't buy cheap PLCs ... you can ... but because with traditional PLCs the moment you start adding any special I/O functions the cost skyrockets. Consider temperature measurement. How much does a typical RTD or thermocouple module cost for a PLC? Try $199 (well known low cost mail order PLC vendor, 4-channel thermocouple module). With a SPLat MMi201 you get 2 channels of temperature measurement free, as part of the controller. The MMi201 also includes an operator interface, with LCD, for $269 total price.

Single Board Computers

Conventional single board computer solutions are likewise weighed down by the cost of adding real-world interfacing. They also have, in the main, very large memories and powerful processors. Great if you need the grunt, but otherwise a costly overkill. It's a bit like paying $1000 for a lawnmower because it comes only with a V8 engine, only to discover the grass catcher is another $600 and they forgot to make the attachments for it on the mower.

SPLat

The various SPLat solutions form a continuous band of cost versus quantity graphs. Our standard products features a lot of built in functionality. Where we have a suitable SPice board, or if we make you one, you achieve tremendous value.

Custom SPLat starts to compete with standard SPLat around a few hundred per year. Below that it may still be a cost-effective solution compared to non-SPLat strategies, but it will depend on the specific circumstances.


Custom SPLat: The development process

This chapter outlines the most important process steps and various considerations in developing a custom SPLat. This is the reflection of 25 years company experience in managing development projects. It will give you a preview of what to expect if you decide to ask us develop a custom SPLat controller for your product. Remember, our sales depend on your sales, so we are well motivated to provide solutions that will work best for your business.

We have a great deal of experience in managing development projects. That is why we take great pains to very accurately define the terms of the relationship ahead of time. You can also rest assured that we are seasoned professionals who know not to bite off more than we can chew. In 25 years we have never had a development project that we failed to complete with a satisfactory outcome.

Previous Topic

Next Topic

In This Chapter

Custom SPLat: The specification and your needs

Custom SPLat: Prototypes

Custom SPLat: Integration

Custom SPLat: Programming

Custom SPLat: Responsibilities and warranties

Custom SPLat: Agency approvals

Custom SPLat: How much will it cost?

Custom SPLat: How to get started


Custom SPLat: The specification and your needs

One of our core skills is establishing customers' needs. We usually devote a lot of effort to properly establishing exactly what it is you need from a controller. That involves a lot of to and fro discussion, be it by email, 'phone or face to face. During this stage we need your trust and patience, so we can 'drill down' and uncover the true situation. This is also our main chance to discover and propose opportunities for cost reduction and/or performance enhancing ideas. The best cost savings result when a customer gives us room to explore the widest possible range of opportunities

The end result will be a functional specification, embedded in a development proposal, that you sign off on before the design starts. This must also include all relevant regulatory standards.

Realistically this stage can take 4-8 weeks. It is also the most important stage.

Getting it right: Specification talk-fest.


Custom SPLat: Prototypes

Normally we will deliver two lots of prototypes. The first, called Alpha Test Prototypes (ATP) are early prototypes that you use to verify for compliance with the specification and to test inhouse. Once you sign off on the ATPs we make any corrections and prepare the Beta Test Prototypes (BTPs). These you incorporate into examples of your product and put out to friendly users to field test.

Once you have thoroughly evaluated and signed off on the BTPs we can commence the production ramp-up.

The ATPs usually take 6-10 weeks, depending on complexity and prior work load. The BPTs can take 2-6 weeks after 'Alpha Sign-off'. Your testing time is additional, and could range from days to months depending on the product (beta testing an air conditioning controller mid-winter would be rather pointless!)


Custom SPLat: Integration

When it comes time to integrate your custom SPLat into your product, we will provide whatever level of assistance you need. Some customers ship us a copy of their product. Sometimes one of our engineers will visit the customer's plant to help with physical installation, wire dress, external devices etc. Some customers are perfectly happy with drawings and notes, and maybe a few phone calls.


Custom SPLat: Programming

The program that goes into a SPLat board to make it do a specific task is called SPLatWare. This serves to differentiate it from the programming tools (software) and the board's underlying 'operating system' program (Firmware)

The SPLatWare is treated as a separate issue to the hardware. This leaves the flexibility for you to do your own if you wish, for us to do it (under a separate programming contract), or for some hybrid arrangement. A lot of customers like to have us do the initial version to get them going, and then take over themselves for any ongoing upgrades. This means you can get up to speed quickly with our help and thereafter be independent.


Custom SPLat: Responsibilities and warranties

During the whole development process there are several points where you sign off on our work. The principle here is that you must assure yourself that the controller you are going to build into your product is suitable for your product. Our design warranty, on the other hand, is that for 12 months we will correct in the design any design errors that surface (and are missed by all your alpha and beta testing and by our testing). A design error is where the design fails to perform according to the specification agreed to at the outset.


Custom SPLat: Agency approvals

Agency (regulatory) compliance and approvals are a vexing subject. We serve customers all over the world, and requirements vary from region to region and from industry to industry. We have a great deal of experience with gaining approvals in many countries.

We will design to comply with the technical standards we are advised of prior to quoting for the development. We may ask you to supply copies of the relevant standards.

In most jurisdictions a product must be approved in its totality. Just because the individual parts are all individually approved or certified it does not mean the final product is automatically approved. How you package our controller within your product will affect its ability to comply. For example, a metal case will reduce radio emissions, whereas inappropriate lead dress may make the controller it very susceptible to noise. Therefore you will at some stage have to take on responsibility for the the overall technical compliance of your total product. We will be available to assist you with technical backup during that process, and we will make any necessary adjustments to our design free of charge (assuming they relate to the standards in the specification).


Custom SPLat: How much will it cost?

The upfront cost (NRE) of a custom SPLat will depend on a number of factors: Complexity, possible re-use of the design or parts of it, any research we need to carry out, etc. Rest assured, however, that the NRE cost of a custom SPLat is significantly less than development of a full custom embedded controller with comparable capabilities, up to 75% less (in one case 90% less!).

The actual production units will in all probability cost you significantly less than the same functionality cobbled together from competing off the shelf products. The potential for savings comes mainly from the fact that there is usually a lot of waste when you use standard products. You may for example need only one channel of thermocouple input but the standard module has 2 or 4 channels. It may also be that there is some function you need that simply is not available in off-the-shelf products. The closest you can get in cost to a custom SPLat is usually a full custom (non-SPLat) controller, but the very much higher NRE of a full custom controller, not to mention the design risk, offsets any benefits in all but the largest volumes.

From our point of view there is a trade off between charging NRE and assured ongoing production quantities. When we put a proposal to you for a custom SPLat, we will set out the actual NRE cost and give you a budgetary unit price for various quantity levels.

The key to cost-effective control solutions is to combine as much of the functionality as possible into the 'core' controller itself. This is what makes a fully integrated custom SPLat so cost-effective.


Custom SPLat: How to get started

The best way to get started is to use our contact page to provide us with a description of what you wish to achieve in a controller. Ideally, we'd like a description of your product. If you are hesitant about that (we'll eventually need to know, but we will gladly sign any reasonable Non Disclosure Agreement), at least try and describe each input and output and what's attached to it. If you are using any special purpose sensors or actuators, we will eventually need technical data on them. If you can, describe any time (speed) critical parts of the control, anything requiring large amounts of memory, analog accuracies and any functions you need other than straightforward timing and sequencing (e.g. modem communications, data logging voice response or whatever). It's a good idea to include 'phone numbers, including a cell-phone.

If all the above is a bit overwhelming, don't worry. Start off by telling us as much as you can, and let us take over from there. We will help you find the answers.


Custom SPLat: Add some SPice

SPLat is quite unique in the world in that we offer a half-way house between standard product and custom products. SPice is a tiny add-on board that sits piggy-back on a main SPLat controller, and endows it with some special-purpose functionality. This contrasts with competing products like industrial PLCs or or single board computers in that the SPice board is fixed to the main board without the need for any card cages, backplanes or other expensive hardware.

Example SPice functions include:

  • Water level detection (conductivity switches)
  • AC voltage measurement
  • Current transformer

What the SPice concept means for you is that you have two possibilities for tailoring a controller to your specific needs, even for a small quantity:

Standard SPLat + standard SPice

Our range of standard SPice add-on boards is growing. That means there is a better and better chance we will have the add-on you need. If we don't have it, and the function you need is something we can see being attractive to other customers, we'll make it (we may ask for a modest minimum quantity order to help defray our NRE).

Standard SPLat + custom SPice

If you need a function that's way out in left field, we can still help you even if we have to charge you an NRE. Our customers find this service is cost effective for quantities down to 10 or 15, even as a once only batch.


SPLat Circuit Element Library

The SPLat Circuit Element Library (SCEL) is a library of pre-designed circuit elements that can be incorporated into various SPLat controllers. In the main these will be found in custom SPLat boards, but they may also be implemented on SPice add-on boards or in other physical formats. The main idea behind SCEL is that each time we design a special function circuit we characterize it, document it and make it available for future designs.

Each of these circuit elements was designed for a specific use. The information provided is based on that design, usually taken from a prototype. Whenever we design a circuit element we optimize it for the particular application it is to be used in. It may be that you have an application that can use a circuit element exactly as presented here. It may also be that you could use something similar but not identical. Please ask us about it!

Previous Topic

Next Topic

In This Chapter

SCEL: CCS125 125mA current source

SCEL: DFRO1 Mid-rail referred ratiometric output

SCEL: IPO8 isolated potentiometric output

SCEL: TM08 thermistor temperature circuit

SCEL: RTD16 Platinum resistance temperature circuit


SCEL: CCS125 125mA current source

The SCEL:CCS125 is a SPLat Circuit Element Library design for driving (sourcing) an analog constant DC current of 125mA full scale into a ground (0V) referred load. It provides a non-isolated output, with the load current sourced from the positive supply. A typical application would be a hydraulic proportional valve. The circuit can be fitted with a reversing relay.

The circuit has two particular features/attributes:

  • To allow the load to be grounded, there is no output current sense resistor. This leads to somewhat lower accuracy and linearity than would be achieved with a current sense resistor. The advantage is that a short to ground from either load terminal cannot produce an uncontrolled high current in the load. A current mirror design results in good accuracy despite this.
  • As designed the circuit is optimized to drive 125mA into a load of up to 24Ohms, developing up to 3V across the load, from a supply voltage between 10V and 28V.

The circuit was initially designed for a proportional hydraulic valve in an automotive application. It was driven off the 3.7kHz PWM output on an MMi99 SPice connector.

The circuit appears to the SPLat programmer as an analog output. Linearity, scale factor accuracy and temperature stability are good, with some non-linearity at very low outputs.

In the following characteristic data the "typical" figures are results of prototype testing. The "worst case" figures are our estimates of worst case performance, but are not guaranteed. When negotiating an implementation of this, or any other, circuit element in your custom SPLat controller, you must identify the parameters that are most critical.

Parameter, 10-28V supply | Typ | W/C
Output current range (full scale value) | 123.5mA | 122mA/ 128mA
Full scale current change versus load change 0Ohm to 24Ohm, 24V supply | -0.2mA |
Full scale current change versus supply change 10V-28V, 24Ohm load | +0.4mA |
Full scale current change versus temperature 25° to 60°C, 24Ohm load, 24V supply | -0.1mA |
Linearity error (deviation from best fit straight line). 24Ohm load, 24V supply, full output range. | <0.4% F.S. |
Linearity error (deviation from best fit straight line). 24Ohm load, 24V supply, between 5% and 95% of output. | <0.12% F.S. |
Settling time to 1mA for full scale step | <50mS |


SCEL: DFRO1 Mid-rail referred ratiometric output

The SCEL:DFRO1 is a SPLat Circuit Element Library design that provides a mid-rail referred ratiometric voltage output. It was designed to drive a voltage controlled hydraulic valve requiring a control voltage which varies between 25% and 75% of the supply voltage (e.g. Danfoss).

The circuit appears to the SPLat program as an analog output and a digital enable output. When disabled the output voltage goes to midrail. When enabled the output varies between approximately 20% of supply for a zero analog output to approximately 80% of supply for a full scale analog output. (Hence the output range is 5% either way in excess of the 25%-75% expected by the valve). The circuit works well with supply voltages from 10V to 28V. It is designed for a load with an equivalent circuit of 12kOhms to midrail.

In the following characteristic data the "typical" figures are results of prototype testing. For the tests the circuit was driven off the 3.7kHz PWM output on an MMi99 SPice connector (8-bit resolution). The "worst case" figures are our estimates of worst case performance, but are not guaranteed. When negotiating an implementation of this, or any other, circuit element in your custom SPLat controller, you must identify the parameters that are most critical.

One point to keep in mind is that there is no analog output value that corresponds to exactly ½ scale. There are exactly as many values below ½ scale as above, but no value at ½ scale. For example, in an 8-bit system, there are 128 values below ½ scale and 128 above. The mid-scale value would be 127.5. For this reason, the mid-scale offset figures quoted are the value that would be obtained if the analog output was 127.5. That said, the circuit could be supplied with a trimpot for setting a precise mid-point value.

Parameter, 10-28V supply, 12K load | Typ | W/C
Output span, relative to half-supply | ±30% | ±27% to ±33%
Expected load resistance | 12K to mid-rail |
Output offset, disabled | ±0.2% |
Output offset, enabled, hypothetical 127.5 counts input | ±0.3% |
Output span change versus temperature 25° to 60°C. | 0.1% |
Linearity error (deviation from best fit straight line). 12V supply, full output range. | <0.15% F.S. (10mV) |
Settling time to 0.5% for full scale step | <200mS |


SCEL: IPO8 isolated potentiometric output

The SCEL:IPO8 is a SPLat Circuit Element Library design for simulating a potentiometer connected to the control input of some external device. It provides an isolated output. A typical application would be a speed control input. This is a DC output circuit, and is not suitable for AC output applications such as isolated audio volume control. The circuit is suitable for inclusion on a custom SPLat controller or on a SPice board.

The SCEL:IPO8 has 3 connections to the outside world. 2 of these are for an externally supplied reference voltage (corresponding to the "top" and "bottom" of the simulated potentiometer) and the third is the "wiper" of the simulated potentiometer. Within the operating limits of the circuit, the "wiper" voltage will equal the DC reference voltage times the ratio command supplied by the SPLat program. The "ratio command" usually means an analog output instruction where the value put out is treated as a proper fraction (i.e. <1) of the full scale value.

The circuit was initially designed to simulate a control potentiometer on an Engine Control Module (ECM). It was driven off the 3.7kHz PWM output on an MMi99 SPice connector. In that case the reference voltage was 5VDC (supplied by the ECM) and the wiper voltage range of interest was 1V to 2V with a wiper load current of 6mA. The resultant circuit performs well with output voltages between 0V and 3 to 3.5V (depending on loading). It has good linearity over the active range, low output impedance and a low temperature sensitivity. Response time is in the 100mS range.

In the following characteristic data the "typical" figures are results of prototype testing. The "worst case" figures are our estimates of worst case performance, but are not guaranteed. When negotiating an implementation of this, or any other, circuit element in your custom SPLat controller, you must identify the parameters that are most critical.

Parameter (5V external reference voltage) | Typ | W/C
Output voltage range, unloaded | 0.09V to 4.33V |
Output voltage range, 470 Ohm load | 0.04V to 3.84V |
Output voltage change at 50% command, unloaded to 470Ohm load | 70mV |
Output voltage change at 50% command, 25° to 60°C | 5mV |
Max output voltage deviation from theoretical value within output range 0-3V and 25° to 60°C, unloaded | 92mV |
Max output voltage deviation from theoretical value within output range 0-3V and 25° to 60°C, 470Ohm load | 44mV |
Current draw on reference voltage, any command value, unloaded | 1.33mA | 2mA
Current draw on reference voltage, any command value, 470Ohm load | 6.34mA | 11mA


SCEL: TM08 thermistor temperature circuit

Thermistor circuit pic

The SCEL:TM08 circuit will provide modest accuracy and resolution temperature measurements at very low cost. The underlying premise of this design is that very many control applications require temperature measurement over a restricted temperature range. For example, if you are making a washing machine you may be quite happy to measure temperatures between 60°C and 100°C, with a nominal target temperature of 85°C.

Normally you would source your own NTC thermistors. Very likely you will need some special encapsulation or mounting. Naturally, we'll attempt to suggest suitable thermistors, and we do know of some already packaged industrial type units.

The circuit used to interface the thermistor is quite simple (hence the low cost). We will scale this circuit for your specific needs during the definition phase of your design. Because thermistors are non-linear, you may need to linearise the raw readings to get °C or °F. This is quite a simple matter with SPLat, thanks to the built in floating point arithmetic. There is a tutorial in the SPLat Knowledge Base, and a supporting spreadsheet that generates the linearising polynomial.

This circuit, in various versions, has been used on several SPLat boards, including MMi99, SL99, SPice10200 and AJ18.

The following characteristic data is for two typical applications. When negotiating an implementation of this, or any other circuit element in your custom SPLat controller, you must identify the parameters that are most critical.

 | Appn. 1 | Appn. 2
Description | Food storage & chilling | Industrial parts cleaner
Temperature range | -3°C - +40°C | 10°C - 110°C
Critical temp range | 0°C - 6°C | 75°C - 90°C
Accuracy | 0.2°C @ calibration point | 2°C
Resolution, critical range | 0.13°C | 0.5°C
Resolution, full range | 0.5°C | 1.34°C

Application 1 was very critical around the optimum storage temperature. In such cases it may be necessary to calibrate the system close to the critical temperature.

Application 2 is not at all critical, so no calibration was performed. The basic accuracy then achieved is a function of the grade of thermistor and of circuit errors.

Resolution (the smallest detectable change in temperature) is a function of the analog to digital conversion resolution. For low cost applications we provide 8 bit conversion, and the above figures are based on that. By adjusting circuit parameters we can optimize the circuit to get best resolution around the critical temperature.

Thermistors provide the lowest cost temperature measurement, both because the thermistor devices are very cheap and because the circuitry needed to support them is very simple. Surprisingly, thermistors also provide very good accuracy relative to sensor price. Thermistors are available for around $5 that have <0.1°C inherent accuracy, and for less than $1 more ordinary thermistors still have inherent accuracies of 1°C or so. This compares very favourably with semiconductor sensors that often have accuracies of 2°C or worse.

"Ordinary" thermistors have working temperature ranges from -50°C to +180°C. One company makes units that go up to 700°C.


SCEL: RTD16 Platinum resistance temperature circuit

RTD circuit pic

The SCEL:RTD16 circuit will provide high accuracy/resolution temperature measurements economically. It is designed to work with industry standard Pt100 or Pt1000 sensors in either 3-wire or 4-wire configurations.

The circuit used to interface the RTD combines a very high resolution analog to digital converter chip with a ratiometric front-end design to produce high accuracy, resolution and stability. We will scale this circuit for your specific needs during the definition phase of your design. Because RTDs are slightly non-linear, you may need to linearise the raw readings to get °C or °F. This is quite a simple matter with SPLat, thanks to the built in floating point arithmetic.

In the following characteristic data are typical figures to give you an idea of the performance you can expect. When negotiating an implementation of this, or any other circuit element in your custom SPLat controller, you must identify the parameters that are most critical.

Temperature range | -0°C - +150°C
Resolution | 0.01°C
Accuracy without calibration | 0.5°C (depends on the grade of RTD used)
Minimum temperature | -50°C
Maximum temperature | +250°C

A lot of the above depend on your choice of RTD sensor. We do not supply sensors.


Tutorials, application notes and white papers

In this chapter of the SPLat Knowledge Base you will find a growing collection of materials that apply to several SPLat products, or which are included simply because they are relevant to control systems in general.

If you are just starting out with SPLat this is not the place to start! The material presented here is mainly "advanced". Start out with the Mini Tutorials in SPLat/PC. You can download the latest SPLat/PC here.

Elsewhere in the SKB ...

Onboard LCD

Onboard counter type A

Product documentation: Controllers

Product documentation: Expansion boards

Product documentation: SPice add-on boards

The SPLat Expansion Framework

Onboard Quadrature Counter

SPLat/PC programming software

Previous Topic

Next Topic

In This Chapter

Beginner's guide to controls

How SPLat works

Tutorial: MultiTrack

Serial communications

Tutorial: Debugging SPLatWare using serial data

Tutorial: SimpleHMI programming of SPLat controllers

Tutorial: SPLat Call Home (SX10509) programming

SPLat Master to Kaspro Demo

Tutorial: Subroutines

Non Volatile Extended Memory (NVEM) User's Guide

RealTime Clock

Tutorial: PID control with SPLat

Tutorial: Semaphores

Tutorial: Using SPiceX for large I/O counts

Tutorial: Thermistor temperature measurement

Tutorial: Multitasking (Superseded)

The SPLat Expansion Framework

Xwire

Cutting and pasting code from the SPLat Knowledge Base


Beginner's guide to controls

This guide has been written with the primary aim of assisting Original Equipment Manufacturers (OEMs) with no prior experience of electronics controls. If you make, or are about to make, a product that needs an electronic control system, and you are unsure of how to procure the control system, this guide is written for you.

I will be covering a wide range of topics, though generally not in any great depth. The idea is to give you an overview of the main issues involved, as a first step towards enabling you to make better informed decisions about the controls for your product. Hopefully you will be able to get through the whole guide in 2-3 hours without feeling your eyes glaze over too many times.

Tip: Have some strong coffee on hand, just in case.

Our mission is to sell a particular brand of controller. Expect some bias. However, don't be too surprised at an occasional unbiased description or comparison of competing product classes.

What you will get out of it

The guide is designed to give you an overview of electronic control systems, including

  • What they do;
  • How they work;
  • Sensors and actuators;
  • How you need to involve yourself in the implementation process (even if you delegate the actual "doing");
  • Various types of controller;
  • Selecting the best controller for your needs

What you need before you start (prerequisites)

  • A need to know (motivation);
  • An understanding of how your machine (product, apparatus) works physically (mechanics, chemistry, dynamics).

How to read this guide (and stay sane)

There is a lot of material presented here. Some of it may very well go deeper than you need or can understand, especially if your knowledge of electrical matters is limited. Don't let that put you off. Read what you can absorb. Skim the rest so maybe you can at least learn a few of the technical terms or recognize certain issues that will need addressing. After that, consider this as a reference source you can go back to as and when specific issues arise. If you are like me you learn best when you have a need for something, which is far better than learning from costly mistakes!

If you are a SPLat customer (Some gentle propaganda)

Here at SPLat Controls we take great pride in being able to help customers with little or no knowledge of control systems or electronics to procure good controls for their products. That is reflected in the vast amounts of educational and training materials we provide, including this guide, and in an 87% customer satisfaction rating recorded by an independent consultant's survey in 2004.

For bona fide customers or prospective customers, we always attempt to assist with issues of matching the best controller to an application. If you lack the engineering expertise to match specifications between controller and connected sensors and actuators, we can assist. We will still need you to supply specifications on the items outside our controller, and if you have studied this guide you will be better equipped to seek out that kind of information and maybe do initial screening.

However, there are caveats:

  1. It must make commercial sense. We can obviously provide more assistance to a 1,000 unit per year customer than to a casual 1-off buyer.
  2. We can only assist you to the degree that we receive accurate information from you.
  3. While we will assist, unless a signed agreement exists to the contrary, nothing we say is to be taken as professional advice. You remain responsible for the outcomes. It is your responsibility to either educate and inform yourself or to seek independent expert advice.

This guide is still under development. Please watch this space!

Previous Topic

Next Topic

In This Chapter

Controls: We are here to help

What does a controller do?

How does it do it?

What you need to work out for yourself

Controller types


Controls: We are here to help

We have a long track record of giving our customers a competitive edge with controls that are a smarter, better fit to their application and which maximize the value added to their product. Whether you make air conditioners, water filtration plants, packaging machines or any other equipment that requires controls, providing you make that equipment in some quantity we are here to help you.

We understand

We understand that most of our customers are not experts at control systems. That is why we make a point of being much more than just suppliers of electronic controls. We provide qualified customers with support that goes way past generic product applications support.

We help

We can help you define and clarify your needs both in terms of specific hardware and general functionality.

  • What can a controller do for your product?
  • How do you define the program?
  • How far do you go with advanced features?
  • Are there things you may not have thought of or may not realize are possible?
  • What are your needs in electrical terms? In shape and size?

Our many years of experience have taught us how to home in quickly on the most important issues. That experience is available to you as a SPLat customer.

Getting started

Getting started is really easy. Just contact us through our website using the general contacts page or use our free needs assessment form.

Are you already a controls expert?

If so, you will appreciate the fact that you can communicate directly with the designers of SPLat, professional to professional. You will get the real information you need, minus BS, to extract the maximum benefit from SPLat. See How SPLat Works for a taste.


What does a controller do?

Before answering the question "What does a controller do?", let's define one key term:

For the purposes of this guide a machine is any man-made apparatus that manipulates "stuff" in the physical world with a particular intended outcome. Hence a machine may be anything with motors, levers, pneumatic cylinders, solenoids, moving parts, pumps, heaters, compressors, sprays, conveyors, valves, tanks etc. etc. The only criteria are that it has been built with a purpose in mind. It needn't look like a "machine". A waste water treatment plant does not, to my eyes, look like a machine, but for our purposes here, it is a machine. The same for a turnstile, a fish smoker or a spa bath.

The main criterion is that the machine must need, or be able to benefit from, an electronic controller.

The purpose of a controller, what it does, is to control the behaviour of the machine in order to achieve a desired result. In order to do this, the controller takes in information about the state of the machine and its immediate environment and based on that information gives out control signals to direct the physical actions of the various parts of the machine.

  • The INformation that feeds into the controller comes in through its INputs.
  • The resulting OUTputs of the controller produce OUTcomes in the physical world.
  • The inputs to the controller come (in general) from sensors. Sensors convert physical quantities such as movement or temperature into an electrical quantity that the controller can use.
  • The outputs from the controller go out to actuators. Actuators convert electrical quantities into physical form such as movement or heat.
  • The relationship between the inputs (the state of the world) and outputs (attempting to influence the state of the world into a desirable state) is defined by the program. The program embodies a set of rules and procedures that someone (the programmer) has determined will result in the required machine behaviour.

This leads us to the following definition of what a controller does:

A controller takes inputs from external sensors, processes them through its program and produces outputs to actuators, with the purpose of producing the desired behaviour of a machine.

In the next section we will take a closer look at how these elements (inputs, program, outputs) work, and add in some more detail about the internal workings of the controller and how some of the external bits (sensors and actuators) work.


How does it do it?

In this section we will look at how the major parts of a control system work, covering both inside and outside the controller itself. The aim is to give you a basic understanding of commonly used terms and practices.

Previous Topic

Next Topic

In This Section

Inputs and outputs

Things outside the controller

Things inside the controller

Programming


Inputs and outputs

From the point of view of the controller, inputs are electronic circuits that allow information from real-world sensors (inputs) to be converted to a form a program can understand. Similarly, outputs are circuits that can convert information produced by a program into electrical signals that in turn can control physical actuators.

The mystery in all this lies in the transition from electrical form to "something the program can understand" and visa versa. In actual fact, the whole controller works in the electrical domain, so electrically there is no great mystery at that boundary. There is no real boundary, just a circuit that converts voltage levels between external levels (say 12V or 110V) and the levels used inside the controller chips (5V or less).

The rest of this section is dedicated to describing various types of inputs and outputs. These are divided into:

  • "digital", which represent on/off quantities (with no stages in between),
  • "analog", which represent continuously variable quantities and
  • "specialized", which gives me an excuse to discuss some interesting instances.

Previous Topic

Next Topic

In This Section

Some common terminology

Digital inputs (simple on/off)

Digital outputs

Analog inputs

Analog outputs

Specialized outputs


Digital inputs (simple on/off)

A digital input circuit on a controller can only recognize two states, ON or OFF. A digital sensor is a sensor that can only have two output states (guess what they are called?). The output of the sensor feeds the input of the controller.

A good example of a digital sensor is a limit switch or a float switch. The interior light switch in your refrigerator is a kind of limit switch. It may be simple but it provides a very useful function.

Simple digital (on/off) sensing is popular because it is relatively cheap. There are a huge number of on/off digital sensors and signal sources on the market.

  • Push button switches
  • Limit switches
  • Electronic proximity switches
  • Photo electric detectors
  • Float switches
  • Position encoders (rotary or linear, incremental, absolute or quadrature)
  • Light level switches
  • Weight overload switches
  • Pressure switches
  • Over-temperature switches
  • Burglar detectors
  • Various purpose made sensor circuits, including very specialized "home grown" or custom designs.
  • Remote control radio receivers
  • Alarm clock snooze buttons (incredibly hard to find in the dark).

Digital on/off sensors come in great variety of electrical output specifications, as do controller input circuits. It is important that the sensor output and the controller input circuit be properly matched.

The next few topics cover the most common digital sensor interfaces.

Previous Topic

Next Topic

In This Section

Industrial sensors: NPN and PNP

Dry contacts

Voltage generating sensors

TTL/CMOS sensors

Open collector sensors

Optically isolated inputs


Industrial sensors: NPN and PNP

A large number of industrial sensors are available on the market. These include proximity switches, photoelectric switches and many other more esoteric types.

The vast majority of industrial sensors will comply with what is known as "NPN" or "PNP" interface conventions. The NPN and PNP sensor output acts as an electronic switch.

NPN interface

In an NPN interface, when the sensor turns on (sensing something), a circuit is closed between the output pin of the sensor and circuit common (ground, 0V). When the sensor is off its output is effectively an open circuit.

PNP interface

In a PNP interface, when the sensor turns on (sensing something), a circuit is made between the output pin of the sensor and the positive supply voltage. When the sensor is off its output is effectively an open circuit.

Typically these sensors can carry 25-50mA on their outputs.

Which is best?

The digital input circuits on most SPLat controllers, standard and custom, are NPN compatible, i.e. designed to work with NPN sensors. We make that design choice simply because it costs less to make an NPN input than a PNP input. That is the pro-NPN argument.

The pro-PNP argument is a bit more subtle: If the insulation on the input wire from a sensor to a controller should fray, the most probable failure mode is a short circuit to circuit ground, i.e. to the metal body of the machine. This will register as an activated (ON) input in an NPN circuit. In certain designs this might represent a safety hazard (say if it were a 'door closed' sensor on a jet plane). Some designers therefore prefer PNP sensors because a short to ground will register as an OFF input.


Dry contacts

Dry contacts are "normal" metal contacts such as in a light switch or push button switch. By appropriate wiring a dry contact can usually be interfaced through controller input circuits designed for industrial sensors (NPN or PNP) and through optically isolated inputs. The voltage and current required by the circuitry to detect the state of the contact are provided by the controller.

Danger, low voltage!

There is however one very important warning about dry contacts. It relates to something called wetting current and is a significant cause of long term (and mysterious) equipment failures.

Metal contacts, when exposed to air, have a nasty habit of getting coated in oxides and tarnish. These coatings are electrically insulating. This insulation may prevent current flow, and the system may fail, with possibly disastrous results.

The good news is that some contact materials are less prone to this than others. Also, if there is enough voltage in the circuit, and the circuit can supply enough current, the contacts can clean themselves. What happens if there is enough voltage and current is that the voltage breaks down the very thin insulating layer and the (relatively high) current then blasts away the oxide. That process is called contact wetting. (I don't know why). In the telephone industry it is referred to as sealing, and generally affects crimped cable splices.

Normally the digital input circuits on electronic controllers will apply low voltages (1 to 12V) and low to very low (milliamps or microamps) currents to the external device. If you are using metallic contacts you should always check the low current ratings of the contact against the controller specification. If you can't get hold of enough information go for gold plated contacts, reed switches or bifurcated contacts. Do not select input contacts with high current ratings on the theory that more is better.


Voltage generating sensors

Some sensors have outputs that generate different voltage levels depending on whether they are ON or OFF. The controller can differentiate between these two levels. Its input circuits would be described as "voltage sensing".

Typical voltage generating sensors are:

  • Quadrature encoders and digital potentiometers (frequently available with a choice of output types)
  • Custom made or highly specialized sensor circuits from companies with limited exposure to industrial markets (and hence a lack of appreciation for how "things are normally done").

In general, it can be risky to try and connect a voltage generating sensor to a controller designed for NPN, PNP or dry contacts. The controller will generate its own voltage, which if applied to a voltage generating sensor could damage the sensor, the controller or both.

For example, a typical voltage generating sensor will use "TTL" levels. That means its internal circuits run off 5V and its normal output voltages will be between 0.2V and 3.5V. If that circuit was connected to a controller that supplies 24V at its input pins, it would probably not survive.

The other way around, connecting a sensor with NPN, PNP or dry contact outputs into a voltage sensing controller, is unlikely to damage anything but will not work. If it does appear to work, beware! it may not be reliable.

It is quite uncommon to see a general purpose programmable controller that is specified to work with voltage generating sensors. That means if for some reason you are stuck with a voltage generating sensor (maybe something that has been custom made for you), you may have a challenge to find a compatible controller. Also, you will have to make a detailed match between the sensor specification and the controller. One solution could be to use a controller with a "programmable comparator" input or use an analog input. Or, if you are having a custom SPLat made, we can accommodate virtually anything.


TTL/CMOS sensors

TTL and CMOS are terms for particular families of electronic logic circuits.

A TTL output will generate two voltage levels: about 0.2V (low) and about 3.5V (high). It can generate the low voltage while conducting several mA from a more positive source (say a resistor between the output and a +5V power supply).

A TTL input expects a voltage greater than 2.4V but less than 5.5V to be applied to it for a high input (anything higher will blow it up). For a low input it expects an applied voltage of between 0V and 0.8V with 1.6mA of current being drawn out of the input pin.

CMOS is generally compatible with TTL, albeit with slightly different voltage levels. That said, in recent years the "standard" 5V power supply system has been replaced with progressively lower and lower voltages, particularly in very high performance computer chips. These days it would therefore be wise to qualify any statement about CMOS compatibility unless it is stated as TTL/CMOS (TTL is always a 5V system).


Open collector sensors

The term "open collector" refers to a particular transistor configuration. It is almost synonomous with NPN. The thing to watch out for is that while an industrial NPN sensor will (almost) certainly be able to withstand 24V on its output, that may not be the case with one described as open collector. You should check the detailed electrical specifications.


Optically isolated inputs

Optically isolated inputs use a device called an optocoupler. An optocoupler consists of a light emitting diode (LED) that gets lit up by an externally applied current, and a photo-transistor that turns on when illuminated by the LED. Because the two need not be electrically connected, there can be a great deal of voltage difference between the LED and the photo-transistor. This provides isolation between the external sensor circuit and the controller circuitry. That isolation can provide an electrical safety barrier or reduce electrical noise coupling.

Typically an optically isolated input will have two input wires. The external circuit (sensor) must apply the right voltage between those wires to turn on the optocoupler. Often the controller will provide a convenient voltage source and configuration options so the inputs can be used with NPN or PNP sensors or dry contacts.

While optically isolated inputs may provide technical benefits in noise reduction and safety isolation (if used correctly!) they tend to add significant cost.


Digital outputs

A digital output on a controller is an output that can only have two states, ON or OFF. Digital outputs are therefore suitable for controlling or driving actuators that similarly are on or off.

Typical on/off actuators include:

  • Lamps
  • Solenoid valves
  • Pumps
  • Relays
  • Contactors
  • Atom bomb detonators

Because controller outputs frequently drive actuators that require significant amounts of power it is important that the ratings of the output match the needs of the actuator. You need to pay attention to:

  • AC or DC
  • Operating voltage
  • Operating current
  • Turn-on and turn-off surges

Here are just some of the traps:

AC coils and DC coils are very different

A mistake we see quite often is someone using a solenoid or contactor with a coil specified for AC, on DC (or visa versa). The fact of the matter is that an AC contactor coil used with DC will draw 10 to 20 times as much current, and will blow up the controller output a few minutes before it burns out itself.

Incandescent lamps draw huge inrush currents

The resistance of a lamp filament varies significantly with temperature. At room temperature it can be as little as one twentieth of its hot resistance. That means when power is first applied the lamp can draw 20 times its normal operating current. That turn on surge can last for an appreciable time, especially in a lamp with a "chunky" filament like a quartz halogen lamp. The surge can destroy a solid state output. The nasty thing is that it may not fail the first time or even the thousandth time it turns on. That means you may have shipped 500 of your machines all over the world before you discover the problem.

Coils kick

When current flows through a coil and is suddenly turned off, the coil generates a large voltage spike of the polarity that seeks to keep the current flowing (it is in the nature of coils to vigorously resist any change in the amount of current flowing through them). If the coil is being driven by DC, a diode connected across the coils will provide a path for continued current flow, and suppress the generation of the damaging voltage spike. Without the diode the spike will almost certainly damage the controller, in some cases beyond salvage. With AC coils a resistor-capacitor snubber may be used.

Previous Topic

Next Topic

In This Section

NPN outputs (sinking)

PNP outputs (sourcing)

Relay outputs

Optically isolated outputs

Voltage generating outputs

Outputs for incandescent lamps


NPN outputs (sinking)

An NPN output is named after the kind of transistor (NPN) that is most commonly used in its circuit. The NPN transistor acts as an on/off switch between the output pin of the controller and circuit common (0V). The load, i.e. device being driven by the output, must be connected between the output pin and the positive supply voltage. Electronics people call this arrangement "sinking", while electrical people will be more comfortable with the term "ground switching".

These outputs can only switch DC, and are frequently limited to voltages less than the DC supply voltage of the controller. They will also have a limited current handling capacity of maybe 250mA to 500mA. When turned on and conducting current, there will be some voltage drop across the transistor.

The NPN outputs on most SPLat controllers and expansion boards can handle up to 400mA continuous, with momentary peaks of 500mA. They drop approximately 1V when conducting. The maximum applied voltage is equal to the supply voltage of the controller.


PNP outputs (sourcing)

Previous Topic

Next Topic


Relay outputs

Previous Topic

Next Topic


Optically isolated outputs

Previous Topic

Next Topic


Voltage generating outputs

Previous Topic

Next Topic


Outputs for incandescent lamps

Previous Topic

Next Topic


Analog inputs

Previous Topic

Next Topic


Analog outputs

Previous Topic

Next Topic


Specialized outputs

Previous Topic

Next Topic

In This Section

PWM outputs

Specialized inputs


PWM outputs

Previous Topic

Next Topic


Specialized inputs

Previous Topic

Next Topic


Things outside the controller

Previous Topic

Next Topic

In This Section

Sensors

Actuators

Serial communications


Sensors

Previous Topic

Next Topic


Actuators

Previous Topic

Next Topic


Serial communications

This section covers serial communications on SPLat controllers. Originally SPLat had a single serial port, and it was intended primarily for programming via SPLat/PC. It can however also be used for communicating with 3rd party devices. In modern terminology a serial port is often referred to as a COM port, a term adopted from personal computers.

More recently designed SPLat controllers have more than one serial port, particularly 32-bit designs.

Serial communications between any two devices requires that there be agreement on two major things:

The electrical interface

The electrical interface defines the voltage levels, pinouts etc of the electrical connection. Common standards are RS232 and RS485.

Some SPLat controllers have RS232 compatible serial interfaces. Others (the models that use our special programming cable) do not, but can be converted to RS232 via a programming cable.

We have a range of RS485 adaptors available, so all SPLat controllers can potentially provide RS485.

Newer designs have a USB programming port.

The protocol

The protocol is the "language" used to convey information across the electrical interface. Just like all people use essentially the same repertoire of sounds interface to each other, there are many different, mutually incompatible, languages.

SPLat controllers support:

  • Our proprietary protocol, SPLatLink, which is used by SPLat/PC for programming and debugging. It is no longer supported as a protocol for user applications.
  • ModBus, which is one of the oldest and most common industrial protocols. SPLat supports "ModBus RTU, master and slave". The majority of commonly used ModBus "functions" are supported.
  • "User programmable" protocols. SPLat contains a fairly open-ended facility for generating and decoding (parsing) the kinds data messages and commands used by a lot of 3rd party devices such as weigh scales, motion controllers, laboratory instruments etc.
  • Other specialised protocols that are implemented using Hash commands and functions. These fall technically under the heading of user programmable protocols, but have been nicely packaged in a "hash programming" wrapper to make them very much easier for you to use. Examples include SimpleHMI and programming 4D Systems' (3rd party) touch screen displays.

________________________________________________________________________

Related topics

Debugging using serial data and the terminal function in SPLat/PC SimpleHMI


Things inside the controller

Previous Topic

Next Topic

In This Section

Counters

Processor

Timers

Memory


Counters

Previous Topic

Next Topic


Processor

Previous Topic

Next Topic


Timers

Previous Topic

Next Topic


Memory

Previous Topic

Next Topic


Programming

Previous Topic

Next Topic

In This Section

How a program works

Programming paradigms

The definition phase

Program design

Coding

Testing and verification

Sequence, the order of events

Simultaneous actions (multitasking)


How a program works

Previous Topic

Next Topic


Programming paradigms

Previous Topic

Next Topic


The definition phase

Previous Topic

Next Topic


Program design

Previous Topic

Next Topic


Coding

Previous Topic

Next Topic


Testing and verification

Previous Topic

Next Topic


Sequence, the order of events

Previous Topic

Next Topic


Simultaneous actions (multitasking)

Previous Topic

Next Topic


What you need to work out for yourself

Previous Topic

Next Topic


Controller types

Previous Topic

Next Topic

In This Section

Hardwired electrics

Traditional PLCs

Programmable relays

Bespoke designs

Single board computers, embedded computers

Stamps

Roll your own microcontroller


Hardwired electrics

Previous Topic

Next Topic


Traditional PLCs

Previous Topic

Next Topic


Programmable relays

Previous Topic

Next Topic


Bespoke designs

Previous Topic

Next Topic


Single board computers, embedded computers

Previous Topic

Next Topic


Stamps

Previous Topic

Next Topic


Roll your own microcontroller

Previous Topic

Next Topic


How SPLat works

This paper is will give you an introduction to the inner workings of SPLat. Most of this is technical stuff you will never need to know. We've made it available to you in case you are curious about the inner workings of SPLat. You may however want to check out the section on programming, which will give you a quick overview of what it's like to program SPLat.

Previous Topic

Next Topic

In This Chapter

How SPLat works: Architecture

How SPLat works: Anatomy

How SPLat works: Programming

How SPLat works: Some history


How SPLat works: Architecture

The following is something you may never need to know, but you may find it interesting. (If it sounds like techno-babble, don't worry. This is not the kind of thing you need to understand to use SPLat!)

The SPLat processor is a virtual machine (VM) implemented in a general purpose microcontroller. Older designs in the current product line-up use an 8-bit Freescale 9S08 family chip, with all the firmware written in assembler. Recent designs (starting 2012) use a variety of 32-bit ARM processor chips with firmware written in C/C++. The VM architecture is based on an 8-bit wide by 4 deep register stack (slightly akin to Forth). It has separate program and data (RAM) memory spaces with 16 bit and 8 bit addresses respectively. Current products expose (depending on the model) up to 64,000 bytes of FLASH program memory and 250 bytes of RAM.

There is a separate 6-deep x 16 bit subroutine return stack, and an 8-bit index register for advanced RAM addressing. The floating point processor contains two 32-bit floating point registers, and implements non-stop arithmetic (e.g. a divide by zero will not crash the processor). Recent ARM based designs contain floating point hardware.

Additional intrinsic functionality is provided by built in counters. Depending on the product model there are

  • One 24-bit high speed hardware counter capable of pulse count and frequency.
  • Eight 16-bit medium speed counters with pulse and frequency
  • One or more 24-bit bidirectional quadrature counters

MultiTrack provides a very effective and processor-efficient cooperative multitasking system. A task queue is implemented using a fixed number of static entries. Each entry stores execution address, pointers and timer information. By not saving registers we extract the maximum use from a small amount of RAM and make context switching extremely fast. As it turns out, the kinds of control tasks SPLat is aimed at are better off with these efficiencies than with resource consuming full-context saving, preemption and task prioritization. The result is a system with 70 microsecond context switching time at 8MHz bus speed. Light, and very nimble! (The 32 bit designs are introducing a separate register set, including the subroutine return stack, per task, and other architectural improvements we couldn't do in the resource limited 8-bit chips.)

For communicating with various peripherals SPLat contains a 20-byte register called U and an associated 1-byte register called V (which holds byte counts for U). U and V act as a transfer buffer for peripheral devices built within the SPLat Expansion Framework.

A separate background subsystem provides external communications, with support for:

  • SPLatLink, our own protocol that's used mainly for debugging and program download
  • ModBus RTU Master
  • ModBus RTU Slave
  • "User" protocol, which lets you program to handle typical 3rd party devices that use serial ASCII data, such as motion control boards or weighers.
  • SimpleHMI, a very easy to program Human Machine Interface


How SPLat works: Anatomy

SPLat controllers are made using off-the-shelf microcontroller chips. We currently use the Freescale '9S08 family of chips, with Flash memory sizes up to 128KB, and a variety of ARM chips.

You write your SPLat program using our programming software SPLat/PC. SPLat/PC must 'translate' the program before you can download it to the SPLat board. The translation process converts your program from text that you can read on your screen into a very compact form the processor chip can use. That form is not the native language of the physical processor. Instead we use a code which is a compacted form of the SPLat language (the technical term is bytecode, as used in Java and .NET). One line of your program, maybe 20-30 bytes, reduces to 1 or 2 or 3 bytes of bytecode.

Inside the processor chip there is a program that we call the 'operating Firmware'. This program is loaded into the chip by us when we manufacture the board, thus turning the general purpose processor into a SPLat processor. Part (a major part!) of this program is the SPLat Virtual Machine (VM). The VM reads the bytecode representation of your program, one instruction at a time, figures out what it means and then goes and does it.

This may seem like a very round about way of doing something. Why not simply translate your program into the chip's native language directly? The main reason is that the bytecode is very much more compact than the same functionality in native machine code. It may take 10-30 native instructions to produce the effect of one SPLat instruction. One test we performed suggest that SPLat bytecode is about 10 times more compact than a leading real-time C implementation. Remember, SPLat instructions incorporate a lot of high level functionality that is fine-tuned for controls applications.

Using bytecode gives another benefit in addition to saving memory: The SPLat VM has control over everything that happens. If it doesn't like something your program is about to do, it can prevent it from happening. A simple example is a floating point division; if you attempt to divide by zero the VM will not "explode", which is what would happen in many other environments. Your program will continue to run. The VM manages the execution of your program and eliminates a raft of possible problems.


How SPLat works: Programming

SPLat is programmed in its own proprietary language. This is a language we created specifically for controller programming. It is a procedural language, in the sense that you specify the individual steps to be taken. For example, to read a named input, read a second named input, AND the two together, send the result to an output, you might use the following code:

         Input    DarkOutside
         Input    WantToRead
         AND
         Output   LightsOn

What makes the SPLat language really outstanding is that it contains many features and instructions that aim squarely at controls programming. An operation that in C or assembler may need five or ten instructions, may only need one or two with SPLat. What this means for you is that SPLat is much easier to program (and to learn and understand) for control applications than general purpose languages.

Special 'bonuses' are our FastTrack programming, which makes simple programs very easy, and MultiTrack, which makes it easy to program several things to happen at once.

From June 2009 you can chose to program most SPLat controllers in C. This choice is suggested only for experienced C programmers.

In This Section

How SPLat Works: Programming tools

How SPLat works: FastTrack programming

How SPLat works: Hash commands


How SPLat Works: Programming tools

A key to the world-wide acceptance of SPLat as one of the easiest to use professional grade OEM controllers is our programming software, SPLat/PC.

SPLat/PC combines a program editor, a simulator, a program translator and a download and debugging tool in one single package. It also incorporates a help file that was historically the main source of documentation and learning for SPLat.

Program Editor

The editor is a Notepad-like plain text editor where you type in your program. Operation is simply and intuitive for anyone familiar with basic Window text editing tools.

Simulator

The simulator consists of a series of windows that simulate various aspects of an actual SPLat controller. These include timers, registers and memory. By far the most useful (and fun) is the I/O simulation window. This allows you to simulate inputs and outputs without having any real hardware present, and means you can often have your program 90% or more tested and debugged before you "go live".

Translator

Once your program is substantially debugged in the simulator, the translator will convert it to the very compact form used by the actual SPLat controller. Most symbolic instructions generate object (bytecode ) instructions on a one for one basis. However, some translate into multiple object instructions. These are mainly the onboard peripheral instructions like OBLCD_Cls, which hide the complexities of programming SPx peripherals.

Download and debugging

Your translated program is downloaded from SPLat/PC to the actual SPLat controller via a serial data cable. For many SPLat models this is a standard serial cable you can buy in your local computer store. For some models we save cost on the controller by using a simple proprietary cable that requires a once-off investment of about $25.

Before you actually run your program in the controller, the I/O simulator window allows you to exercise and check out your target machine manually, activating relays and solenoids from the computer screen and monitoring inputs on the screen.

Help file & tutorial

The help file in SPLat/PC was at one time the sole repository for all documentation on SPLat. It is still the place to go to for instruction and guidance on the internal workings of SPLat (the basic registers) and intermediate programming knowledge (very basic training is now delivered through our interactive online training course and the mini-tutorials that are built into SPLat/PC). Since 2001 all new information has been placed in the SPLat Knowledge Base (of which this article is a part). The older materials, including our much praised "classic" tutorial, are still in the SPLat/PC help file.

Please note: The interactive/online training course is very old. Everything in there is valid and worth learning, especially the underlying principles. However the course pre-dates significant developments, most notably our MultiTrack multitasking technology which makes SPLat programming even easier. So, if you are relatively new to controller programming, the course will give you a good foundation, but please don't think it represents the full SPLat story.

You can download SPLat/PC here


How SPLat works: FastTrack programming

One of the innovations we are most proud of in SPLat is FastTrack programming.

SPLat contains a rich and diverse repertoire of over 400 instructions.Coupled with large program memory capacities and advanced onboard hardware resources, that gives you the ability to implement a wide range of very sophisticated applications using SPLat.

Many applications, however, are straight forward timing and sequencing tasks with the simplicity of say a dish-washer controller. FastTrack programming lets you program these less ambitious applications without having to learn 400 instructions. FastTrack is a carefully devised subset of only 14 instructions that gives you enough programming capability to implement a huge range of simple machine sequencing tasks.

Below is an example of a simple but realistic program written using FastTrack. The application is a clutch operated 'full cycle' flywheel punch press with an ejector for finished parts and a strip steel feeder for raw material.

The press has a clutch, and is arranged such that each activation of the clutch causes the press to go one complete cycle and punch out one part. The press has two limit switches. One detects the 'down' position, actually just past the point where the part has been punched out. The other limit switch detects when the press has returned to the home position, when the clutch should be deactivated.

The ejector is a pneumatic mechanism that simply needs a 200mS 'on' pulse to operate.

The steel strip raw material is fed in through a pneumatically operated feeder mechanism. The feeder has a single solenoid to activate it and a proximity switch that senses the material and indicates the feed cycle is complete.

The purpose of this program is to automatically sequence the press, the ejector and the feeder.

Sample FastTrack program

Roll your mouse over each line of code in turn to display an explanation.

Step1:  On       Press
        WaitOn   DoneLimitSwitch
        On       Ejector
        Pause    20
        Off      Ejector
        WaitOn   UpLimitSwitch
        Off      Press
        On       Feeder
        WaitOn   FeedSwitch
        Off      Feeder
        Goto     Step1

Press:  oEQU     5

;This is a comment
;This is another comment

The above example is a simple program that performs only one task. With the introduction of MultiTrack in December 2005, you can now extend this simple programming method to having several virtually independent programs running at the same time in the one SPLat controller, but able to coordinate their actions as required.


How SPLat works: Hash commands

Hash commands were introduced in January, 2012. Hash commands, and their associated hash functions, provide a very high level of functionality in specialised areas.

Hash commands are made possible by a pre-processor which manipulates your program before it is sent to the translator. The pre-processor picks out all lines flagged with a # and generates SPLat code and NVEM0 table entries required to perform the required function. This allows complex operations to be reduced to a simple line of code.


How SPLat works: Some history

The first SPLat was designed in December 1995, on the back of a custom controller design we did where the customer needed the ability to do some final on-site programming. That was actually an echo of a scripting language we created for Chubb Fire alarm panels back in 1987.

The first SPLat was the SP10-8, which became the SP108. It used a Motorola MC68HC705C9 processor running at 4MHz, with an external serial eeprom for the user program. SL88, OEM36, AJ18 and PMC3 were similar.

More recently the MC68HC08 became available and affordable (typically, our customers need cost effective solutions, not V8 power). This chip has onboard Flash, which stores both our operating Firmware and the user program. It also runs at 8MHz and uses half as many clock cycles, so what with faster onboard program memory, faster clock and more efficient instructions, second generation SPLat controllers are up to 7 times faster than 1st generation designs. These product include MMi201, SL99, MS12 and numerous custom SPLat controllers.

In December 2005 we introduced MultiTrack programming. MultiTrack technology "lowers the bar" on programming multitasking applications to the point where a complete novice can be using it with only a few hours of study.

The current (Mid 2013) generation of SPLat controllers uses MC9S08 controllers at up to 20MHz. The chips have 128K flash and 8K of RAM. We are in the process of transitioning to 32-bit ARM processors, which offer much larger memories and faster operation at an affordable price.


Tutorial: MultiTrack

MultiTrack is a mechanism that allows you to very easily write SPLat programs that can do many things at once.

MultiTrack replaces the older multitasking technique based on the Suspend and Resume instructions. While Suspend and Resume instructions still exist, and can still be used, MultiTrack is easier to understand, easier to use and easier on processor resources.

MultiTrack makes it very easy to write several programs that can run simultaneously and independently of each other in one controller. You need to apply one or two simple rules and write a tiny bit of code to "glue" it all together, but once that's done it's a breeze. MultiTrack also provides an excellent foundation for libraries of re-usable code.

With MultiTrack we have:

  • Extended the capabilities of the FastTrack instruction sub-set to provide true multitasking capability.
  • Added a versatile timing mechanism, called SuperTimers, that covers the whole range from 10mS to 46 hours in a single unified way, and allows a large number of simultaneous timers (limited only by available RAM memory).
  • Added an easy to use but very powerful mechanism for controlling several identical "channels" in a controller, say for a 4-head bottle capper.
  • Expanded the FastTrack concept of instructions that wait for an event (like WaitOn) to cover a wide range of events.
  • Extended the timing functions of Pause, WaitOnT, WaitOffT and WaitOnKT to a full 46 hour range.
  • Added buffering to the OBLCD device (LCD display) on the MMi201 to make it easier to share between multiple tasks.

The end result is that SPLat becomes even easier to program, and allows less experienced users to write more sophisticated programs

This tutorial is divided into 3 levels: Basic, Intermediate and Advanced, so you can work up to the level that suits your experience and needs.

Outcomes (what you will get out of this tutorial):

Basic:

Teaches you the basic mechanics of MultiTrack.

  • Extend the simplicity of FastTrack programming into programs with several things happening at once.

Intermediate:

Let's you start to structure programs to unlock the full power of MultiTrack

  • Use semaphores for inter-task synchronization. You will be able to have several tasks running at once, but able to coordinate between themselves.
  • Extend beyond FastTrack timing using the new SuperTimers, allowing you to wait for several events, not just one, and up to 46 hours, not just 5 minutes. It's only slightly more complicated to use than WaitOnT,
  • Understand the relationship between the MultiTrack mechanism and subroutines and registers.

Advanced:

Gets into some of the more sophisticated stuff.

  • Create multiple extended timers
  • Measure intervals from 10mS to 46 hours.
  • Speed up SuperTimers to simplify debugging of processes that take hours or days.
  • Build multi-channel applications (the same code operating on several sets of data and I/O)
  • Use transient tasks (tasks that live just long enough to do their job, then fade away and release memory for other things)

Prerequisites (what you should know before you start):

Basic:

  • You should understand FastTrack programming of SPLat. That is covered the mini-tutorials built into SPLat/PC and in the older FastTrack tutorial in the SPLat/PC help file. You will appreciate MultiTrack the most if you have been struggling to create programs that do several things at once (SPLat has always been capable of that, but MultiTrack makes it ever so much easier).
  • You should know how to drive SPLat/PC, including editing, single stepping and setting breakpoints.

Intermediate:

  • An understanding of automatic RAM allocation.
  • Semaphores

Advanced:

  • Indexed addressing (or data arrays in other languages)
  • Use of SPLat registers
  • Floating point operations

Previous Topic

Next Topic

In This Chapter

MultiTrack (Basic): Quick-start for Dummies

MultiTrack (Basic): The YieldTask instruction

MultiTrack (Basic): How many tasks can I have?

MultiTrack (Intermediate): Synchronizing tasks

MultiTrack (Intermediate): SuperTimers

MultiTrack (Intermediate): Subroutines and registers

MultiTrack (Intermediate): Using the OnBoard LCD

MultiTrack (Advanced): Multiple simultaneous SuperTimers

MultiTrack (Advanced): Measuring elapsed time with a SuperTimer

MultiTrack (Advanced): Speeding up SuperTimers for debugging

MultiTrack (Advanced): Changing the 10mS time base

MultiTrack (Advanced): Accuracy of SuperTimers

MultiTrack (Advanced): Transient tasks

MultiTrack (Advanced): Multi-channel operations

MultiTrack (Advanced): Permitting infinite loops.


MultiTrack (Basic): Quick-start for Dummies

If you are in a real hurry, this section may be all you need to get started with using MultiTrack. This is especially so if you have been limiting yourself to the FastTrack instructions and now want to get SPLat to do several things at once. The MultiTrack mechanism greatly expands what can be achieved using little more that the 14 FastTrack instructions. You will need to learn a few more instructions (just 2 to get going), but it's very worthwhile!

Let's illustrate this with an example. As usual, we'll do some rather pointless light flashing, because right now I want you to focus on the mechanisms, not the logic of the application.

Suppose we want a simple FastTrack program which just flashes output 0, on for 430mS and off for 870mS, endlessly. This will do it:

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

SlowFlash:      On          0
                Pause       43
                Off         0
                Pause       87
                GoTo        SlowFlash

Cool! The only snag is that this program will tie up the whole SPLat processor (a.k.a. SPLatty) just flashing a light. Suppose we want to really get bold and simultaneously have output 1 flashing at a quite different rate, say 110mS on and 50mS off. That could get messy! But thanks to MultiTrack, it's simple.

Here's how we do it, using MultiTrack:

Firstly we start to think of each of the two flashers as tasks. A task is like a program within a program. It sits there continuously performing a particular set of actions, independently of anything else in your program. A complete control program may consists of a number of tasks taking care of individual bits of the overall functionality, such as managing a pump or an operator interface.

In our simple flasher application the first task is already written. It's called SlowFlash. Let's do the second one and call it FastFlash:

FastFlash:      On          1
                Pause       11
                Off         1
                Pause       50
                GoTo        FastFlash

All we need now is something to tie the two tasks together. Here it is (I won't re-type the two tasks from above):

;Start of program
            LaunchTask  SlowFlash
            LaunchTask  FastFlash

            RunTasksForever

;The code for the two tasks goes in below here ...

What the LaunchTask instruction does is to schedule the task that starts at the named line (SlowFlash or FastFlash in this case) for later execution. It does not actually execute any of the task code, it simply builds a queue (list) of tasks (which we call the task queue) that will be executed later.

The RunTasksForever instruction then sets the task queue running. That means that each of the tasks in the queue is "run", in turn, in a round-bobbin sequence.

You can best understand what happens by running the program in SPLat/PC. Here's the whole program in one block you can copy and paste into SPLat/PC (you will need SPLat/PC V7.16.0 or later. You can download the latest SPLat/PC here).

;Start of Dual flasher program

               LaunchTask  SlowFlash

               LaunchTask  FastFlash

               RunTasksForever

;==== SlowFlash task ==============

SlowFlash:	On          0

               Pause       4

               Off         0

               Pause       8

               GoTo        SlowFlash

;==== FastFlash task ==============

FastFlash:	On          1

               Pause       1

               Off         1

               Pause       5

               GoTo        FastFlash

I have reduced the time intervals for you, so the program will give reasonably noticeable flashing in simulation mode.

Try single stepping the program and observe what happens. You will notice a lot of jumping between a Pause in one task and a Pause in the other task. What is happening is that when a task gets run, and is executing a Pause that has not yet timed out, it immediately yields. What yielding means is that because the task has nothing to do, it frees up the processor (SPLatty) to do something else (switch to another task). Because one tasks yields as soon as it has nothing useful to do, and thereby lets SPLatty do something in another task, we get the illusion of SPLatty doing several things at once. If you look carefully you will see that the status bar at the bottom of the SPLat/PC editor window tells you when the program is in MultiTrack. The action of switching from one task to another is called task switching or task swapping.

All the FastTrack instructions that block the processor, i.e. make it wait for something to happen, become non-blocking with MultiTrack. That means you can now write several tasks using instructions like WaitOn, Pause and WaitOffT, string them together using LaunchTask and RunTasksForever, and it will just work. Later you will learn how to make tasks communicate with each other via semaphores and other RAM variables so they can synchronize and coordinate their activities.

Exercise:

Add a third task to the sample program above. Call it Toggle. Toggle should monitor input 0. When input 0 comes on Toggle should turn on output 3. Toggle should then wait for input 0 to turn off, and then wait for it to turn on again a second time, then turn output 3 off. This should repeat indefinitely. If a push button switch is connected to input 0 it will act as a push on, push off switch for output 3. If your recognize this as the same tired old example I always trot out, don't worry. It's the simultaneous running of several tasks that counts, not what it does. Better still, if you don't like my example devise your own!


MultiTrack (Basic): The YieldTask instruction

In the previous lesson I showed you how potentially blocking instructions like WaitOn and Pause will automatically cause a task switch, i.e. cause the task to yield so another task can run. But what if there are no such instructions being executed?

The YieldTask instruction lets you force a task switch anywhere you like in your program. This is something you have to think about, for two reasons:

  1. If a task never yields, no other task will get a chance to run.
  2. If a MultiTrack task executes more than 255 instructions without yielding, the controller will suffer a fatal runtime error (we made it that way to force you to do the right thing!)

To illustrate this, copy the following into SPLat/PC and run it:
(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

                LaunchTask  TaskA
                LaunchTask  TaskB
                RunTasksForever

TaskA:          On          0

                Pause       1

                Off         0

                Pause       1

                GoTo        TaskA

TaskB:          IncM        0

                GoTo        TaskB

The program will "bomb out" after a short while. A YieldTask placed after the IncM instruction will allow it to run properly. Try it!

Here's an example of a task that uses YieldTask. This task will monitor inputs 2 and 3 simultaneously. If input 2 comes on it increments RAM location Count. If input 3 comes on it decrements Count. Having counted up or down it waits for both inputs to be off, then repeats.

CountFunc:       GoIfInOn    2,CountUp

                 GoIfInOn    3,CountDn

                 YieldTask               ;Give someone else a go

                 GoTo        CountFunc   ;Loop and repeat

CountUp:         IncM        Count

                 GoTo        WaitForBothOff

CountDn:         DecM        Count

WaitForBothOff:  YieldTask

                 GoIfInOn    2,WaitForBothOff

                 GoIfInOn    3,WaitForBothOff

                 GoTo        CountFunc

Count:           mEQU        10            ;Declare the RAM variable

Exercise 1:

Combine the above task with the dual flasher example in the earlier example and get it running. Take time to study what the YieldTask is doing.

Hint: You can monitor Count at location 10 in the Data Memory window in SPLat/PC (explore the Window menu if this is unfamiliar ground).

Exercise 2:

The first loop of CountFunc (the first 4 instructions) uses one instruction more than necessary. Rearrange it so it only use 3 instructions. Hint: Move the YieldTask to the start of the loop, eliminate the unconditional GoTo and modify the remaining two conditional GoTo's so it works. Note: In general you should not stress over minor inefficiencies like this one in your code. In fact, unless you are pushing the limits of available memory, some inefficiencies are a good investment if they make the code easier to read.

Exercise 3:

Consider the following rewritten snippet of code:

WaitForBothOff:  WaitOff     2

                 WaitOff     3

                 GoTo        CountFunc

This would seem to achieve the same result with a saving of 2 lines of code. However, the original will only "trip" when both inputs are off simultaneously. Work out why the the above snippet could "trip" with input 2 on and input 3 off.


MultiTrack (Basic): How many tasks can I have?

The number of concurrent MultiTrack tasks that can be supported by a controller can depend on the model of controller and the Firmware revision it contains. The documentation for each controller contains a MultiTrack topic that defines its capacity.

Typically the number is 32 concurrent tasks.


MultiTrack (Intermediate): Synchronizing tasks

When you write a MultiTrack program containing several tasks, each task will normally be designed to take care of some fairly well defined aspect of the program. However, you will frequently need the various tasks to interact with each other, to coordinate their activities.

A simple but powerful way of coordinating the activities of multiple task is to use semaphores. Semaphores are simple on/off flags that can be set, cleared and tested by your program. Think of the semaphore signals used along railroad tracks, coordinating the activities of trains running on multiple tracks. Just as railroad semaphores synchronize the activities of the various trains to produce good outcomes rather than bad ones, semaphores can be used to coordinate the activities of tasks in a MultiTrack system.

Example

Imagine you are writing a program to control a batch processing vessel in the food industry. The vessel has an inlet valve, a heater and a pump to pump out the processed liquid. The heater is controlled by a simple thermostat but must only be turned on while the vessel is full. The fluid level in the vessel is controlled by low and high level probes. In addition there is a dispensing pump that must run for 10 seconds to dose the liquid with an additive during filling. The process is started by an input called iStartButton.

The program will have 3 tasks:

  1. The main sequencer. It controls the inlet valve, the processing time (3 minutes, timed from when the temperature reaches the setpoint) and the pump-out action. It also uses semaphores to "master control" the heater and the additive pump.
  2. The heater control. It controls the heater using the thermostat input and a semaphore from the main sequencer. It signals back to the main sequencer when the temperature gets to the setpoint, using a different semaphore.
  3. The additive pump control. It runs the additive pump for 10 seconds when commanded by the main sequencer via a semaphore.

The complete program file is located in the directory path Examples>MultTrk under SPLatPC in Program Files. The file name is MulTrk01.SPT.

Apart from the MultiTrack instructions that have been introduced earlier in this tutorial, it uses one other new MultiTrack-related instruction, WaitForST.

WaitForST (Wait For Semaphore True) will simply sit and wait for the nominated semaphore to become True. It will continually yield until the condition is met. Once the condition is met is lets control pass to the next instruction in the task.There is also a complimentary instruction, WaitForSF, which waits for the semaphore to become false.

Let's dissect the main features of the program, one task at a time...

The main sequencer

Roll your mouse over each line of code in turn to display an explanation.

MainSequence:
        WaitOnK         iStartButton
        On              oFillValve
        WaitOn          iFullProbe
        Off             oFillValve
        SetS            sHeaterEnable
        SetS            sAdditiveEnable
        WaitForST       sAtTempSetPoint
        Pause           18000
        ClrS            sHeaterEnable
        On              oDrainPump
        WaitOff         iEmptyProbe
        Off             oDrainPump
        GoTo            MainSequence

Heater control

The heater control task is best explained in terms of small blocks of code, each corresponding to a different phase in the life and times of a cycle.

sHeaterEnable           defSEM
sAtTempSetPoint         defSEM

Heater: WaitForST       sHeaterEnable

        On              oHeater
        WaitOff         iThermostat
        Off             oHeater
        SetS            sAtTempSetPoint

HeatLoop:
        YieldTask
        GoIfSF          sHeaterEnable,HeaterDone
        Input           iThermostat
        Output          oHeater
        GoTo            HeatLoop

HeaterDone:
        Off             oHeater
        ClrS            sAtTempSetPoint
        GoTo            Heater

Additive pump

sAdditiveEnable         defSEM

AdditivePump:
        WaitForST       sAdditiveEnable
        On              oAdditivePump
        Pause           1000
        Off             oAdditivePump
        ClrS            sAdditiveEnable
        GoTo            AdditivePump

Exercise 1:

If the start button is pressed in the middle of a cycle, when the cycle completes a new cycle will start immediately. Work out why this is so, and change the program so the button must be pressed after the end of the cycle to have any effect.

Exercise 2:

The code in the heating loop in the heater control could be a little smaller. The loop contains one conditional GoTo and one unconditional GoTo. Rewrite the loop to eliminate the unconditional GoTo.

Exercise 3:

Run the program in the SPLat/PC simulator. You can get the complete program in the directory path described above. If it's not there you may need to download and install the latest SPLat/PC. You will probably discover that testing the complete program in toto is quite impractical. Break the program into individual tasks and test each task independently, as best you can. You will have to write some special code to aid testing.


MultiTrack (Intermediate): SuperTimers

A companion feature to MultiTrack is the SuperTimer. The SuperTimer feature provides for timing and measuring intervals from 10mS to over 46 hours in a single, consistent mechanism. Features include:

  • 10mS resolution with 46 hours range (24 bit timer)
  • Available inside and outside of MultiTrack
  • The number of timers that can be running is limited only by available RAM memory
  • Every task in MultiTrack task has one re-usable SuperTimer which is accessed via simplified instructions, and can have more by using RAM.
  • Simple interval generation as well as measurement
  • A speed-up mechanism to simplify debugging

Note: When executed inside a MultiTrack task, the FastTrack instructions with built in timing (Pause, WaitOnT, WaitOffT and WaitOnKT) use the task's SuperTimer.

We have also converted the older timing instructions like Pause, WaitOnT etc so that in dialect 16 and later they too have a range of 10mS to 46 hours.

In this lesson I will show you how to use the SuperTimer that is available in every MultiTrack task. In later lessons you will see the more advanced features.

The basis of the SuperTimer feature is a 46hour/10mS system timer that is running continuously. A time interval is generated by capturing the value of the system timer at the start of the interval and then waiting for it to have advanced by the required amount from the captured value. It turns out that if the math is done carefully (the controller takes care of all that automatically, nothing for you to worry about), even if the system timer rolls over from max to 0 during the interval, providing the interval required is less than the capacity of the timer (46 hours), everything works out OK. The caveat is that you must make sure your program can never attempt to generate or measure a single interval in excess of 46 hours (actually 46h36m12.16s)

Example: The following code is a rewrite of the main sequencer from the example in the previous lesson. I have made two functional changes:

  1. The cook time has been increased from 3 minutes to 3 hours (1,080,000 times 10mS)
  2. There is a stop button to allow the cycle to be manually cut short.

The two instructions involved in the actual timing are MarkTime and LoopIfTiming.

MarkTime simply captures the current value of the System Timer and stores it in an special register associated with each MultiTrack task. LoopIfTiming has two arguments. The first is the required interval, in this case 1,080,000. The second is where to jump to if the interval has not expired since the last MarkTime in the current task.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;===== The main sequencer  ============================================
MainSequence:
        WaitOnK         iStartButton    ;Wait for start command
        On              oFillValve      ;Start filling
        WaitOn          iFullProbe      ;Wait for full
        Off             oFillValve      ;Stop filling
        SetS            sHeaterEnable   ;Signal the heater to start
        SetS            sAdditiveEnable ;Signal the additive pump to run
        WaitForST       sAtTempSetPoint ;Wait for vessel to get up to temperature
        MarkTime                        ;Note the time
;In the CookLoop we sit endlessly testing for timeout and also testing the stop
;button. We do a YieldTask each time around to free up the processor for other things
CookLoop:
        YieldTask
        GoIfInK         iStopButton,CookStop
        LoopIfTiming    1080000,CookLoop
CookStop:
        ClrS            sHeaterEnable   ;Stop the heater
        On              oDrainPump      ;Start draining
        WaitOff         iEmptyProbe     ;Wait for it to empty
        Off             oDrainPump      ;Stop draining
        GoTo            MainSequence    ;Go back and wait for the next cycle

Exercise 1:

Graft the above code into the previous example program. Save the result with a different file name and then test it. You may find it useful to set a breakpoint at the YieldTask instruction.

Exercise 2:

There are two hazards in the above functional changes. The first is the danger of a previously latched press of the stop button. The second is the danger of stopping the process while the additive pump is still running. It may not be a good idea to be running the additive pump while the vessel is draining. Fix the problems. Hint: You will need to change the additive task over to using a SuperTimer.


MultiTrack (Intermediate): Subroutines and registers

There are some restriction you must be aware of when using MultiTrack, particularly if you are advanced enough to be using subroutines and/or the processor registers. These restrictions come about because the amount of RAM available in SPLat processors is limited, and in implementing MultiTrack we had to make certain design decision to preserve RAM. Therefore there is just one subroutine return stack and one set of main registers (data stack, I, R, floating point registers, "legacy" timers 0-7) shared across all tasks.

In practice these restrictions are not very onerous. The reality is that in the majority of applications that SPLat is intended for (controlling machines and other "apparatus") there are other capabilities that are much more important, such as the simplicity of real-world interactions brought about by instructions like WaitOn.

Subroutines

You can call subroutines (GoSub or any of its relatives) inside a MultiTrack task, but you cannot yield from inside a subroutine. That means the subroutine must run to completion and Return, and may not contain any of the yielding instructions like YieldTask, Pause, WaitOn, WaitOffT etc. The SPLat/PC simulator will raise an error if it detects a violation, and the actual board will suffer a fatal runtime error.

Registers

Because there is only one set of registers, you should never rely on a register remaining unchanged when a task Yields and then gets run again later. It is impossible to make SPLat/PC or the controller itself able to detect violations of this rule, so you must simply take care in your program design.


MultiTrack (Intermediate): Using the OnBoard LCD

With dialect 16 we have added full buffering to the OBLCD on the MMi201 (though not the MS12). That means that when your program displays something, the display data is written instantly to a dedicated memory area (which happens very quickly) and is then transferred to the actual LCD device "at leisure". That way your program is not held up while the relatively slow transfer of data to the LCD takes place.

There are still a couple of things you have to watch out for when using the OBLCD in MultiTrack:

  1. If a task yields between setting the cursor (OBLCD_SetCur) and displaying something, another task might move the cursor in the meantime and scramble the display. The best policy therefore is to set the cursor and display the data without allowing an intervening yielding instruction.
  2. Displaying numbers, especially floating point numbers, takes quite a bit of of processor time. If you update number displays very frequently you will slow down the whole program.


MultiTrack (Advanced): Multiple simultaneous SuperTimers

With MultiTrack each task gets a "free" SuperTimer for setting intervals. This was covered in an earlier lesson. But what if you need to have several timers running simultaneously within a task? You will need to manually create additional SuperTimers.

Remember: When executed inside a MultiTrack task, instructions with built in timing (Pause, WaitOnT, WaitOffT and WaitOnKT) use the task's SuperTimer.

To manually create SuperTimers within a task (or to use SuperTimers outside MultiTrack tasks) you need to set aside 3 bytes of normal RAM (Data Memory) for each timer. The directive for doing this is

MyTimer:              defTIME24

Thereafter 'MyTimer' can be used to refer to the specific SuperTimer. defTIME24 sets aside 3 bytes (24 bits) of RAM to hold the time value.

To start timing with a manual SuperTimer you use the instruction

        STStart       MyTimer

What this does is to grab the current system time value and save it in RAM in the 3 bytes of MyTimer.

To determine if a certain amount of time has elapsed since the STStart instruction was executed, you use the STTest instruction:

        STTest        MyTimer,1234567

This will return True in X if the interval (1234567 times 10mS) has not yet expired, i.e. it is still timing the interval. This is consistent with the legacy timers and the Test instruction.

Example:

This example is a little contrived, but it illustrates the point. When a start button is pressed, turn on output 7 and start timing a 1 minute interval. When the same button is pressed a second time, start flashing output 0 on for 500mS and off for 500mS for the remainder of the minute. At the end of the minute make sure outputs are off and repeat the process.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

OverallTime EQU         6000                    ;1 minute in 10mS increments
MyTime      defTIME24                           ;Memory for SuperTimer

Start:

        ResetK                                  ;Clear input latches

        Off             7                       ;Turn off 1 minute indicator

        WaitOnK         0                       ;Wait for 1st button press

        On              7                       ;Indicate that we are timing

        STStart         MyTime                  ;Start 1 minute over-arching timer

Loop1:

        GoIfInK         0,StartFlashing         ;Test for 2nd button press

        STTest          MyTime,OverallTime      ;Test if the minute has expired

        GoIfT           Loop1                   ;Keep trying if the minute is not up

        GoTo            Start                   ;Timed out ... end of the game

;Flash output 0 for the remainder of the minute.

StartFlashing:

        On              0                       ;Flash on

        Pause           50                      ;Time the ON period

        Off             0                       ;Flash off

        STTest          MyTime,OverallTime      ;Test if the minute has expired

        GoIfF           Start                   ;G/ no longer timing

        Pause           50                      ;Time the OFF period

        STTest          MyTime,OverallTime      ;Test if the minute has expired

        GoIfF           Start                   ;G/ no longer timing

        GoTo            StartFlashing

This program has been coded without using MultiTrack. You will notice there are several tests for expiration of the 1 minute time interval. This is necessary because there are several places in the program where the program is in a loop but the minute could expire.

Exercise 1:

Get the program running in the SPLat/PC simulator. Hint: Reduce the time intervals to speed it up. You can also close the Pause timer window to speed it up.

Exercise 2:

Convert the program to a MultiTrack task and get it running. You will most likely get an error message first time you run it. Fix the problem. Notice that the Pause timer window no longer appears. This is because when executed from inside a MultiTrack task Pause is generated by the SuperTimer mechanism, but  when not in a MultiTrack task it is generated by an older mechanism (a single, global Pause timer).


MultiTrack (Advanced): Measuring elapsed time with a SuperTimer

There are times when you will want to measure how much time has elapsed since some event. Maybe you want to display an elapsed time on the fly or determine how long it took to get up to temperature. To achieve this you need to capture the start time using STStart (which means you need to declare a 3-byte timer variable using defTIME24), and subsequently get the amount of elapsed time using fSTTimeSince. What fSTTimeSince does is to calculate the amount of time that has elapsed since the moment captured by STStart and leave the result in floating point register W. The number in W will be in units of 10mS.

Example:

The following example is a stop watch written as a MultiTrack task to run in an MMi201 with LCD. The complete program file is located in the directory path Examples>MultTrk under SPLatPC in Program Files. The file name is MulTrk02.SPT.

There are a few interesting features in this program:

  • The stop watch task is written as a Finite State Machine (SPLatMap).
  • There are three states: Waiting for the start button, Timing (waiting for the stop button) and waiting for the reset button. The line labels reflect the state numbers, which is all part of a scheme for producing consistent labeling.
  • In the timing state (state 1, SW_1) the display is updated every 100mS. This will produce enough display activity to satisfy the user that the watch is running, without burdening the processor too much. Cunning use is made of a WaitOnKT instruction to monitor the stop button and time the 100mS display refresh interval in one single instruction.
  • ResetK is used in each state to guard against inadvertently stored, latched button presses.
  • The actual elapsed time determination and display is done in a subroutine. There is a simple floating point calculation to scale SuperTimer counts (10mS increments) into seconds.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

        LaunchTask      StopWatch
        RunTasksForever

;----- StopWatch task ---------

;I/O is set up for an MMi201

iSW_StartButton         iEQU    12

iSW_StopButton          iEQU    11

iSW_ResetButton         iEQU    10

SW_Timer        defTIME24       ;Reserve RAM for the stopwatch timer

StopWatch:

SW_Set0:

        OBLCD_SetCur    0,0

        OBLCD_Text      "  0.00"                ;Clear stopwatch display

SW_0:

        ResetK                                  ;Clear Key latches

        WaitOnK         iSW_StartButton         ;Wait for start command

SW_Set1:

        STStart         SW_Timer                ;Start timing

        ResetK                                  ;Clear Key latches

;Main timing..

;While the SuperTimer takes care of the actual timing, we monitor

;the stop button, breaking off every 100mS to update the display.

SW_1:

        WaitOnKT        iSW_StopButton,10       ;100mS

        GoIfT           SW_Set2                 ;G/ button was pressed

        GoSub           SW_Display              ;Update the display

        GoTo            SW_1                    ;Keep watching the button

;Here when user hits the stop button

SW_Set2:

        ResetK                                  ;Clear Key latches

        GoSub           SW_Display              ;Display final time

SW_2:

        WaitOnK         iSW_ResetButton         ;Wait for reset

        GoTo            SW_Set0                 ;Repeat

;Subroutine to grab and display elapsed stopwatch time

SW_Display:

        fSTTimeSince    SW_Timer                ;Get time

        fLoadQ          0.01                    ;10mS increments

        fMul                                    ;Scale to seconds

        OBLCD_SetCur    0,0

        OBLCD_fDispW    6,2

        Return


MultiTrack (Advanced): Speeding up SuperTimers for debugging

It can be quite tedious to debug programs with with delays of up to 46 hours. We have therefore included a special instruction to speed up the SuperTimers. It is called

    STScale	Factor

Factor can be a number between 1 and 255. Once that instruction is executed the SuperTimers will speed up by Factor times. For example

    STScale	60

will make the SuperTimer mechanisms run 60 times faster, so 1 hour becomes 1 minute.

There are a few cautions:

  • Only the SuperTimer timing is speeded up. The legacy timers (timers 0 to 7) do not run faster. In SPLat/PC the FastTrack timing instructions (Pause, WaitOnT, WaitOffT, WaitOnKT) when run outside of MultiTrack do not run faster but do inside MultiTrack. In real hardware they run faster inside and outside MultiTrack. This could produce wild results if you mix timing mechanisms.
  • The speed-up will inevitably produce some inaccuracies. For example, with high speed-up factors, short delays can become essentially zero.
  • A factor of 0 will stop all SuperTimer timing.


MultiTrack (Advanced): Changing the 10mS time base

There is a second mechanism for changing the speed of the SuperTimers. This was introduced in dialect 20, August 2007. You can change the basic 10mS SuperTimer clock interval to some other value from 1 to 255mS. Set U0 to the required value in mS and then execute a

         SPxCmd1         1,!CPU

This affects all timers that use the SuperTimer mechanism, which is any timer that can have a duration counter up to 16,777,215 (Pause, fSTTimeSince, WaitOnT, etc). If you set the interval to 1, the timing will run 10 times faster than normal. If you set it to 256, the timing will run 25.6 times slower than normal giving you a timing range of 49.7 days. Due to the resolution of the internal math, the re-scaling will produce an error of up to 0.2% at the slow end of the scale. The actual error will be the error produced by rounding 65,536/U0 to the nearest integer. If U0=0 it reverts to the default 10mS.

Note that it makes little sense to run the SuperTimers with a clock that is faster than the "loop time" of your program.


MultiTrack (Advanced): Accuracy of SuperTimers

The SuperTimer mechanism has been designed to avoid problems of cumulative timing errors due to program latency when using MultiTrack.

The following program illustrates the point. If you run this in an MMi201 (or earlier) as it is, the 1 second flasher will manage just 58 flashes in a minute. In a more recent controller this will become closer to 59 flashes, because the processor is faster. By comparison, if you remove the very first line and let Flash1S run as a MultiTrack task, the flasher produces as close to 60 flashes per second as can reasonably checked with a watch.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

        GoTo            Flash1S
        LaunchTask      Flash1S
        RunTasksForever

Flash1S

        On              7

        GoSub           SlowIt

        Pause           5

        Off             7

        fRecallW        fCounter

        fInc

        fStore          fCounter

        OBLCD_SetCur    0,0

        OBLCD_fDispW    5,0

        Pause           95

        GoTo            Flash1S

SlowIt

        fLoadW          1.111111

        fLoadQ          0.00001

        SetMem          Counter,100

SAA1

        fAdd

        DMGNZ           Counter,SAA1

        Return

Counter         defBYTE

fCounter        defFLOAT

So, what is happening????

The subroutine SlowIt has been designed simply to waste time, about 40mS in an MMi201. If Flash1S is run outside the MultiTrack task queue, that 40mS adds directly to the 50mS generated by Pause 5, giving a total output ON time of 90mS. So, instead of 1000mS total period we get 1040mS, or 57.6 flashes per minute.

When we change the program so Flash1S is a MultiTrack task, our super timer magic comes into effect. The SuperTimer is generated via a system timer that counts up 10mS ticks. That timer runs all the time, and is used for any timing outside the task queue. Inside the task queue, however, it is different. Each time the task queue is to be run, the system timer is copied to a separate buffer register, and that same, static value is used for all SuperTimer calculations. It is as if time stands still during each run of the task queue. It then jumps forward at the start of the next run, when the buffer register is again updated from the system timer. When the  Pause 5 is first encountered the termination time is calculated starting from the time captured in the buffer.

This scheme would fail to work if subroutine SlowIt took more than 50mS, because SlowIt won't complete until after the Pause 5 should have completed.


MultiTrack (Advanced): Transient tasks

It is not necessary for a task to live forever. There can only be a finite number of tasks in the queue at any point time (how many depends on the model of SPLat board), so it may sometimes pay to make a task that is launched when required and terminates itself when it has done its job. That way you not only save on the finite capacity of the queue, but you also make your program run faster because no processor time is wasted processing idle tasks.

The instruction KillTask causes a task to yield without being placed back into the task queue, effectively killing itself. It is not possible for one task to kill another task. However, tasks may LaunchTask each other.

You must take care not to launch multiple copies (called instances) of a task unless that is your exact intention.

Let's make a push on/push off light switch that flashes a pilot light on output 1 three times each time the button is pressed.

Here's the main toggle function:

Toggle:         WaitOnK         0
                On              0
                LaunchTask      Blink3
                WaitOnK         0
                Off             0
                LaunchTask      Blink3
                GoTo            Toggle

;Toggle assumes the existence of another task called Blink3.

BlinkCount      defBYTE

Blink3:         SetMem          BlinkCount,3

Blink3Loop:     On              1

                Pause           1

                Off             1

                Pause           1

                YieldTask

                DecMGoIfNZ      BlinkCount,Blink3Loop

                KillTask

Exercise:

Build a program around the two tasks listed, with the necessary LaunchTask and RunTasksForever instructions.

Hint: Blink3 is launched from inside Toggle, so you don't need to launch it again.

Hint: In simulation you can speed up execution a lot by minimizing the editor window.

Watchit: When you test your program, be careful not to activate the "button" (input 0) while Blink3 is active. This would cause multiple instances (copies) of Blink3 to be launched concurrently. This will produce unexpected results.

Exercise:

Watch the BlinkCount counter in the Data Memory window. It will normally be allocated to location 19. You will see it go to 3 and then count down to 0 each time Blink3 is launched.

Now try clicking the "button" many times, and watch Blink3 playing up. Observe what happens to BlinkCount. Try and explain what you see (but don't stress if you can't!)

Exercise:

Use a semaphore in Blink3 to protect against multiple instances being launched and attempting to co-exist.


MultiTrack (Advanced): Multi-channel operations

An extremely innovative feature of MultiTrack is its special support for multi-channel operations. Say for example you have 4 identical channels of lighting controls. The multi-channel feature will allow you to use one single copy of your program and very easily process the 4 channels independently.

The magic is done using a special register called J. With J you can launch several copies (called instances) of a task, each with a different value of J. When an instance of a task is executed, the J value for the particular instance is automatically added to all RAM and I/O addresses referenced by the task.

The syntax for launching an instance of a task with a specified value of J is

    LaunchTask	MyTask,3

This will launch an instance of MyTask with J set to 3. In LaunchTask the J argument is optional. If you leave it out (which is what we've been doing up to this point), J is set to 0 and has no effect.

You can also use the LaunchTaskX instruction, which takes the J value from the X register and pops the register stack.

    LaunchTaskX	 MyTask

This will launch an instance of MyTask with J set to whatever number is in X.

Example:

The following program is my boring old light on/light off example. Each time a button on input 0 is pressed, a light on output 4 will alternate its state, i.e. if it's off it turns on, if it's already on it turns off:

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

        LaunchTask      Toggle
        RunTasksForever

Toggle:

        WaitOnK         0

        On              4

        WaitOnK         0

        Off             4

        GoTo            Toggle

All I have to do to change this to a 4-channel switch, controlling 4 lights, is to launch 4 separate instances of Toggle, each with a different J value. The program still only contains one copy of Toggle, but there are 4 instances running, with each instance accessing different I/O points. Here it is ...

        LaunchTask      Toggle,0  ;Handles input 0, output 4

        LaunchTask      Toggle,1  ;Handles input 1, output 5

        LaunchTask      Toggle,2  ;Handles input 2, output 6

        LaunchTask      Toggle,3  ;Handles input 3, output 7

        RunTasksForever

Toggle:

        WaitOnK         0

        On              4

        WaitOnK         0

        Off             4

        GoTo            Toggle

Copy this program into SPLat/PC and study its operation. Display the registers (menu Window>Registers or CTRL+R) and you will see the J register. As you single-step the program you will see J changing. For each of the instructions that access I/O, J is added to the I/O address in the instruction.

If you are familiar with SPLat's index register, I (as in letter 'eye'), the principle of J will be fairly familiar. If not, don't worry. The index register can be used with selected RAM and I/O instructions to alter which address is actually accessed. The number in I gets added to the instruction address. This is called indexed addressing. Similarly, in a MultiTrack task J gets added to addresses. Hence we call it jndexed addressing (pronounce that however you like, I say "jindexed")

  Previous Topic

  Next Topic

In This Section
Multi-channel MultiTrack: Suppressing  J
Multi-channel MultiTrack: RAM addressing with J
Multi-channel MultiTrack: Semaphore addressing with J and I
Multi-channel MultiTrack: Making I behave like J
Multi-channel MultiTrack: BranchJ
Multi-channel MultiTrack: NVEM


Multi-channel MultiTrack: Suppressing J

There may be times when you are making a multi-channel program using MultiTrack, but need to suppress the jndexed addressing. To achieve this we have introduced a new class of instruction modifier, called a precode.

If any memory or I/O instruction is preceded by NoJ: the normal jndexed addressing will be prevented for that single instruction.

A trap for young players!!

If you GoSub from inside a MultiTrack task, the caller's J will be used in the subroutine. This can be trap that leads to hard to find bugs. Be careful when writing single-channel subroutines that may be called from multi-channel tasks. Use NoJ: to prevent this happening.

Example:

Imagine in our push on/push off light switch (yes, that same tired old example again!) we want input 7 to enable the outputs. Hence an output may only be on while input 7 is on.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

        LaunchTask      Toggle,0
        LaunchTask      Toggle,1
        LaunchTask      Toggle,2
        LaunchTask      Toggle,3
        RunTasksForever

Toggle:

        WaitOnK         0         ;Wait for turn-on command

        On              4

Toggle1:

        YieldTask

        GoIfInK         0,Toggle2 ;Test for turn-off

        NoJ:GoIfInOn    7,Toggle1 ;Test shared enable i/p

Toggle2:

        Off             4

        GoTo            Toggle

Copy this program into SPLat/PC and study its behaviour. Use the register display to show you what J is doing. You will see I have changed the WaitOnK that turns off the light into a "Yield loop" which tests both the push button (GoIfInK) and the enable input (NoJ:GoIfInOn).

Exercise 1:

The above program has a flaw. If the enable input (7) is off, pressing one of the buttons (0-3) will result in the light flashing on very briefly. This kind of thing could be a nuisance if you were making a lighting system. It would be more than embarrassing if you were making an explosives detonation controller for a coal mine! Modify the program so no light can come on, even momentarily, while input 7 is off. It must work correctly:

  1. When input 7 is turned on momentarily then off again while the outputs are off. There must be no "carry over" from 7 having been on.
  2. When one of inputs 0-3 is activated while 7 is off. The light must not come on if input 7 is subsequently turned on.
This exercise should illustrate to you how important it is to design and test for every imaginable circumstance. See also our product disclaimer.


Multi-channel MultiTrack: RAM addressing with J

The following program illustrates how J automatically modifies RAM (Data Memory) addressing. The program has 4 channels. When a channel is activated by an off to on transition on its input, the corresponding output flashes 5 (NumberOfFlashes) times. (I made the flashes short so it would be convenient for you to test in the simulator). The flashes are counted in an array of RAM variables called Counter. Counter is declared in a defBYTE directive. The argument 4 makes the defBYTE set aside 4 contiguous bytes of RAM for 4 counters. When Counter is referenced in the program (in the DecMGoIfNZ instruction) J is used to select one of the 4 bytes.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

NumberOfFlashes EQU     5

        LaunchTask      TaskA,0

        LaunchTask      TaskA,1

        LaunchTask      TaskA,2

        LaunchTask      TaskA,3

        RunTasksForever

Counter: defBYTE        4        ;Array of 4 single-byte counters

TaskA:

        WaitOnK         0

        SetMem          Counter,NumberOfFlashes

TaskA1:

        On              0

        Pause           1

        Off             0

        Pause           1

        DecMGoIfNZ      Counter,TaskA1

        GoTo            TaskA

In the above example the address offset is just J. If there was a floating point instruction, the offset would be 4xJ, because each floating point variable uses 4 bytes of RAM. Similarly, for SuperTimers the offset would be 3xJ, because a SuperTimer uses 3 bytes.


Multi-channel MultiTrack: Semaphore addressing with J and I

Most of the time indexed addressing and jndexed addressing produce the same effect when they happen. There is one important exception: Addressing of semaphores.

Indexed addressing of semaphores

The I value is added to the base byte address. That means successive values of I (0, 1, 2, 3 ...) will select the same bit number in successive bytes of RAM.

Jndexed addressing of semaphores

The J value is added to the bit address. That means successive values of J (0, 1, 2, 3 ...) will select successive bits in the same byte of RAM, and progress to the next byte of RAM if necessary. If you are using automatic RAM allocation you would normally not need to worry too much about how it works.

In multi-channel programs you would use a "quantity" (how many items in the array) argument in your defBYTE, defSEM etc declarations.


Multi-channel MultiTrack: Making I behave like J

The precode IasJ: in front of an instruction will force index register I to be applied to that instruction using "J-rules". Now why in the world would you ever want to do that?

There are 3 principal reasons:

  1. J does not exist outside of a task running under MultiTrack. You may however need to access data that is normally (inside MultiTrack tasks) accessed with jndexed addressing. In particular, you may want to do semaphore addressing using bit offsets (J-rules) rather than base byte offsets (I-rules).
  2. You may need to write a task that can freely access all the elements of another task's data arrays. A prime example of this is a setup menu that needs to be able to adjust 4 different temperature setpoints for 4 instances of a temperature control task.
  3. You may want to use indexed addressing on instructions that hitherto did not have indexed versions.

Example:

There are no indexed versions of the floating point recall and store instructions fRecallW and fStore. However, you can force indexing by using the IasJ: precode. The following nonsense program will continuously increment 5 floating point counters stored in RAM locations 20 and up. Run it in SPLat/PC and see how it works. You can display floating point registers and data in the floating point window (CTRL+P).

Loop
        LoadI           5
Inner
        DecI
        IasJ:fRecallW   20
        fInc
        IasJ:fStore     20
        ItoX
        GoIfZ           Loop
        GoTo            Inner


Multi-channel MultiTrack: BranchJ

Although J is automatically applied to all memory reference and I/O instructions inside a MultiTrack task (unless you use NoJ:), there may be occasions when the automatic behaviour is not what you need. The BranchJ instructions gives you an open ended way of forcing exactly what you need to happen.

BranchJ works in a similar way to the other Branch instructions (Branch, BranchM, BranchR). It gives you an "n-way branch" depending on the value of J.

Example:

In this (really exiting! :-) version of our on/off light switch, the order of the outputs is reversed relative to the inputs, i.e. input 0 controls output 3, 1 controls 2, 2 controls 3 and 3 controls 0.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

        LaunchTask      Toggle,0
        LaunchTask      Toggle,1
        LaunchTask      Toggle,2
        LaunchTask      Toggle,3
        RunTasksForever

Toggle:

        WaitOnK         0

        GoSub           Reverse

        iOn             0

        WaitOnK         0

        GoSub           Reverse

        iOff            0

        GoTo            Toggle

Reverse:

        LoadI           3

        BranchJ

        Target          J0

        Target          J1

        Target          J2

        Target          J3

J3:     DecI

J2:     DecI

J1:     DecI

J0:     Return

Exercise 1:

Analyze how the program works.

Exercise 2:

What happens if you change the iOn and iOff instructions with IasJ:On and IasJ:Off respectively?

Exercise 3:

Rewrite the Reverse subroutine for the following mapping, not using a chain of DecI instructions. Do use the BranchJ instruction.:

Input | Output
0 | 1
1 | 6
2 | 7
3 | 0


Multi-channel MultiTrack: NVEM

Some of the the NVEM instructions can also take advantage of jndexing. Specifically these are: NVfReadW, NVfReadQ, NVfWriteW, NVPopByte and NVPushByte. These are the same instructions that have an indexed version.

Example:

In the previous lesson I used a BranchJ to change the mapping between inputs and outputs in the good old on/off light switch example. In this program the mapping is done using a table in NVEM0.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

        LaunchTask      Toggle,0
        LaunchTask      Toggle,1
        LaunchTask      Toggle,2
        LaunchTask      Toggle,3
        RunTasksForever

Toggle:

        WaitOnK         0

        GoSub           MapOutput

        iOn             0

        WaitOnK         0

        GoSub           MapOutput

        iOff            0

        GoTo            Toggle

;---------------------------------------

;Given an input number in J, get the corresponding

;re-mapped output number to I.

MapOutput:

        NVSetPtr        MapTable

        NVSetRecNum     0

        NVPushByte      0       ;Read the table -> X

        XtoI                    ;Transfer it to I

        Return

;----------------------------------

        NVEM0           ;Start of NVEM0 space

MapTable:

        NV0Byte         1,6,7,0


MultiTrack (Advanced): Permitting infinite loops.

If a MultiTrack task executes more than 255 instructions without yielding, the controller will suffer a fatal runtime error. This is a deliberate mechanism designed to prevent you from locking up your program with an infinite loop inside a task. The ClrInstCount instruction resets the counter each time it is executed, effectively defeating the protection mechanism.

Example:

You may have a controller that you want to perform a set of tasks when powered up, but then, once the job is done, go into a dormant state until the next time it is turned off and back on again. The following task could be launched (LaunchTask) by some other task that decides it is time go dormant:

;Go to sleep until powered down and back up
GoodNight:      ClrInstCount
                GoTo          GoodNight

If you attempted that without the ClrInstCount it would fail to work, because after 256 instructions the board would suffer a fatal runtime error, reset itself and run again.

The ClrINstCount instruction is potentially dangerous, for the very reason it can allow you to put the controller to sleep. Use it only if you are very sure of what you are doing.

Note that some of the instructions you may use actually translate into several machine instructions. The 255 instruction limit is machine instructions. Instructions like OBLCD_SetCur are translated by SPLat/PC into several low level instructions.


Serial communications

This section covers serial communications on SPLat controllers. Originally SPLat had a single serial port, and it was intended primarily for programming via SPLat/PC. It can however also be used for communicating with 3rd party devices. In modern terminology a serial port is often referred to as a COM port, a term adopted from personal computers.

More recently designed SPLat controllers have more than one serial port, particularly 32-bit designs.

Serial communications between any two devices requires that there be agreement on two major things:

The electrical interface

The electrical interface defines the voltage levels, pinouts etc of the electrical connection. Common standards are RS232 and RS485.

Some SPLat controllers have RS232 compatible serial interfaces. Others (the models that use our special programming cable) do not, but can be converted to RS232 via a programming cable.

We have a range of RS485 adaptors available, so all SPLat controllers can potentially provide RS485.

Newer designs have a USB programming port.

The protocol

The protocol is the "language" used to convey information across the electrical interface. Just like all people use essentially the same repertoire of sounds interface to each other, there are many different, mutually incompatible, languages.

SPLat controllers support:

  • Our proprietary protocol, SPLatLink, which is used by SPLat/PC for programming and debugging. It is no longer supported as a protocol for user applications.
  • ModBus, which is one of the oldest and most common industrial protocols. SPLat supports "ModBus RTU, master and slave". The majority of commonly used ModBus "functions" are supported.
  • "User programmable" protocols. SPLat contains a fairly open-ended facility for generating and decoding (parsing) the kinds data messages and commands used by a lot of 3rd party devices such as weigh scales, motion controllers, laboratory instruments etc.
  • Other specialised protocols that are implemented using Hash commands and functions. These fall technically under the heading of user programmable protocols, but have been nicely packaged in a "hash programming" wrapper to make them very much easier for you to use. Examples include SimpleHMI and programming 4D Systems' (3rd party) touch screen displays.

________________________________________________________________________

Related topics

Debugging using serial data and the terminal function in SPLat/PC SimpleHMI

Previous Topic

Next Topic

In This Section

Serial communications: Port selection

Serial communications: protocol selection

ModBus

User programmable protocol

RS232 pinouts: a primer


Serial communications: Port selection

In dialect 29 we introduced the ability to use more than one serial communications port. Here's how it works:

  • Serial ports are addressed in the range 251 to 255. These are addresses in the RAM address space that do not actually have RAM "behind them".
  • The "default" port is the controller's programming port. It sits at address 251.
  • There is a whole collection of instructions for manipulating the default port at 251. A typical one of these is ComRx_fGetNum, which retrieves a floating point number from the serial input buffer. Historically these are the original instructions.
  • There is a second set of instructions for accessing any other serial ports that may exist in a given controller. These all start with an 'a', for example aComRx_fGetNum.
  • Most of the ComRx... and aComRx... instructions are about receiving serial data. The ones for transmitting data are those like iifPrintWFW.

When using Hash commands

When using hash commands that reference serial ports (including a USB port that is mapped as a serial port, as on the EC1), it becomes much simpler. Select the serial port using the Port hash function.


Serial communications: protocol selection

SPLat controllers support a variety of communications protocols. This chapter describes the mechanisms for selecting which protocol is to be "in charge". Subsequent chapters provide the details of each protocol.

Switching protocols

Current SPLat controllers have one serial communications port, which was originally intended only for programming the controller. It is not possible to operate more than one protocol on the serial port at any one time. SPLat therefore provides a mechanism for switching between protocols. The same mechanism provides control over the lower level settings like bit rate (a.k.a. baud rate) and character profiles. This "Protocol selection" chapter is about that mechanism.

What is a "protocol"

The serial port on a SPLat controller is capable of sending and receiving a stream of data bit, 1's or 0's, one at a time. Several consecutive bits (typically 10) grouped together form a character. A protocol defines how those characters are to be interpreted. In effect the protocol is the language used for communications between the SPLat and say a computer.

In a typical protocol information is exchanged in the form of messages or datagrams. A datagram consists of a discrete collection of characters, transmitted in sequence, that as a whole convey a particular meaning, command or item of data. A datagram usually also contains some form of error detection, so a corrupted datagram can be detected and retransmitted. The details vary from protocol to protocol. The terminology can also vary from protocol to protocol. For example, in the ModBus protocol the datagrams are normally called queries and responses.

What is NOT a protocol

There tends to be some confusion and mis-information about what constitutes a protocol. These mistakes can lead to serious misunderstandings and ultimately costly mistakes.

  • Most common is the assertion that RS232 and RS485 are protocols. They are not. RS232 and RS485 are electrical interface standards. They define voltage and current levels, and in the case of RS232, connector types and pinouts. Nothing more! They do not define bit rates, character profiles or the structure of datagrams.
  • Another common mistake is to confuse the character profile with the protocol. The character profile defines the number of data bits, stop bits and a parity bit. Those things define the low level makeup of a single character, not what the character is to mean. Think of the character profile as being the font, like Arial or Times New Roman. While the character profiles must match between two devices that are to communicate, they are not the protocol.

Previous Topic

Next Topic

In This Chapter

Protocols: The default protocol

Protocols: Communications Control Blocks (CCB)


Protocols: The default protocol

SPLatLink is our "native" protocol, and is what is used for downloading programs and interacting with the controller when debugging with SPLat/PC. SPLatLink can also be used for one on one communications between a SPLat controller and a PC.

Since writing this we have introduced ModBus support, which provides a richer feature set in a recognized standard protocol. We no longer support SPLatLink for use in customer applications.

SPLatLink is the default protocol. If you do nothing, SPLatLink is what you get. When a SPLat Controller first starts running your program, be it due to power being applied or a restart after a fatal error, the SPLatLink protocol will be in charge.

To invoke a different protocol you must switch protocols. This is done via a programming instruction. However, in the first 10 seconds after a reset (power up or restart after a fatal error) SPLatLink is unconditionally in charge. That means it is always possible to take control of a controller via SPLat/PC by clicking the "Connect" button in the Module window within 10 seconds of powering up the controller.


Protocols: Communications Control Blocks (CCB)

Since this was written we have introduced Hash commands. The Open_Serial hash command make this much simpler to do, so 99% of the time you won;t need what follows

Most protocols have a number of attributes that can be controlled, and need to be set up. This may be fairly obvious things like the Baud rate (strictly speaking not part of a protocol, but a very important setup parameter) or subtler, protocol-specific things.

It takes up to 21 bytes of information (possibly more in the future) to fully specify the settings for a protocol. These bytes get stored by you, the programmer, as a table in NVEM page 0 (if that means nothing to you, don't worry, we will give you examples later). The table is called a Communications Control Block (CCB), and is created as you edit your program ("Set at Design Time").

This discussion is generalized; it does not pertain to any specific protocol.

Once the CCB is set up in NVEM0, it is invoked during program run by executing a ComSetCCB instruction. The ComSetCCB instruction contains an argument that points to the CCB.

In summary, to switch to a protocol other than the default SPLatLink, you need to:-

  1. At design time create a Communications Control Block (CCB) that selects the required protocol and sets its parameters.
  2. When your program runs, have it execute a ComSetCCB instruction that points to the CCB.

In a program, this would look something like the following. Don't try yet to fathom the details of the CCB; that will be explained under each individual protocol. Also, be aware the the CCB structure may vary between protocols, according to their individual needs.

;Example of protocol switching

;The following instruction would be in the body of the program,

;most likely in a program initialization sequence:

        ComSetCCB       ModBus_CCB      ;Point processor at CCB for ModBus

        ...

;The following is in the NVEM0 segment, which must be located

;after all executable program lines (i.e. at the end of the program)

        NVEM0           ;Demarcation between program space and NVEM page 0 space

ModBus_CCB:     	   ;Communications Control Block for ModBus master

        NV0Byte         2       ;Protocol = ModBus RTU Master

        NV0Byte         0       ;Device address (N/A)

        NV0Byte         0       ;Character profile (8,N,1) *Must match slaves!*

        NV0Byte         7       ;Baudrate = 38400 *Must match slaves!*

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         5       ;Message gap x 10mS

        NV0Byte         15      ;Query timeout x 10mS

        NV0Byte         3       ;Query attempts before failure

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

        NV0Byte         255     ;Not used

We mentioned earlier that in the first 10 seconds after a reset, the controller is locked in SPLatLink protocol (as used by SPLat/PC) by a startup timer. The instruction ComTestStartTimer will test if that startup timer is running, and return a True result in X if it is. You can then branch your program using a GoIfT or GoIfF.

If you ignore the startup timer and just do a ComSetCCB, the whole processor will stall (block) until the timer expires. You might write a splash screen (startup message) to the LCD before starting the delay. You could also do anything that is required to "make safe" your machine, and also initialize any values that will be transmitted over the protocol once it starts running.


ModBus

ModBus is a communications protocol that was first designed for use with Modicon brand PLCs, and has become one of the most popular fieldbus protocols, especially in North America.

One of the reasons that ModBus is popular is that it is capable of hiding a lot of the gory details from the user. With ModBus you don't have to worry much about the details of how data gets transferred between two "boxes". Instead you define a mapping between items in one box and the corresponding items in another box. For example, in the above diagram RAM location 198 in the SPLat has been visualized as mapped to the speed control register in the VSD (Variable Speed Drive). Once ModBus is configured the effect would be that whenever the program in the SPLat changes the contents of RAM location 198, the speed of the motor driven by the VSD will change accordingly. Similarly, if the status flag in the VSD gets set (maybe denoting a problem), semaphore 56 in the SPLat will reflect the change. All this happens without the programmer needing to understand the messages that flow between the two boxes, although you must specify the data flow.

In reality it can be a bit more complicated than that, but it is still considerably less complicated than having to actually compose and transmit messages (datagrams), track their progress and handle errors and lost messages.

Masters and Slaves

ModBus is a Master/Slave protocol. What that means is that in a system that uses ModBus there must be one device that is the master and all others must be slaves. Data transfers are always initiated by the master, and a slave may only "speak" when first "spoken to" by the master. One master and one or more slaves share a single communications channel. Normally every slave will hear every message sent by a master. Depending on the type of channel, every slave may or may not hear every other slaves' transmissions.

Each slave must have a unique address in the range 1 to 247.

A typical ModBus "conversation" consists of a query from the master to a slave, followed by the slave's response to the query. A very small number of queries may be broadcast to all slaves in a system using address 0, in which case no response is possible. A query will most commonly be either a request for data from a slave or a transmission of data to a slave. In the first instance the slave's response contains the requested data. In the second case the slave's response is an acknowledgement the the data was received.

If a slave fails to decode a query correctly it stays silent. The master will then note the absence of a response within a certain time limit and take some corrective action. If a query decodes correctly but contains an illegal request (say a bad data address), the slave will send back a special error response.

A SPLat can be configured as either a master or a slave. The two modes have differing programming requirements.

SPLat as a ModBus Slave

It is very easy to program a SPLat to be a slave. All you have to do is correctly configure and activate a Communications Control Block (CCB). This basically configures the SPLat with the right communications parameters to match the master. Thereafter the master takes all the the initiatives and your slave is its obedient ... slave. If the master is a non-SPLat device, you will need to configure it according to its documentation. If you are using a SPLat also as the master, you configure it according to the documentation that follows. In either case you need to know which query types we have implemented in SPLat, so you can configure your master to issue queries the slave SPLat is able to respond to.

SPLat as a ModBus Master

Making a SPLat into a ModBus master is a bit more complicated. Unavoidably you must configure the master to issue specific queries, so you need some understanding of the main query types. However, we give you an easy way of specifying the queries. You can concentrate on the data you want to move between units without worrying about the details of datagram generation, acknowledge timing or lost messages.

Previous Topic

Next Topic

In This Chapter

ModBus: Communication Control Blocks

ModBus: Resource mapping

ModBus: Supported functions

ModBus: Slave configuration

ModBus: Master configuration

Example: Simple PC<==>SPLat comms using ModBus


ModBus: Communication Control Blocks

Since this was written we have introduced Hash commands. The Open_Serial hash command make this much simpler to do.

ModBus is selected and configured via a suitable Communications Control Block (CCB). The following table summarizes the various parameters in the CCBs for ModBus. Entries are hyper-linked to detailed descriptions. If you are using a ModBus protocol you must set up a CCB with the specified parameters and then invoke it in your program with a ComSetCCB instruction.

The table contains a spare column so you can print it out and use it as a work sheet. Click on each entry to get a detailed expose.

Byte | Slave RTU | Master RTU | Notes
0 | 1 | 2 |
1 | DevAddr | 0 |
2 | CharProfile | CharProfile |
3 | BaudRate | BaudRate |
4 | Not used - set to 255 |  |
5 | Not used - set to 255 |  |
6 | Not used - set to 255 |  |
7 | Not used - set to 255 |  |
8 | Not used - set to 255 |  |
9 | MessageGap | MessageGap |
10 | 0 | QryTimeout |
11 | 0 | QryAttempts |
12 | Not used - set to 255 |  |
13 | Not used - set to 255 |  |
14 | Not used - set to 255 |  |
15 | Not used - set to 255 |  |
16 | Not used - set to 255 |  |
17 | Not used - set to 255 |  |
18 | Not used - set to 255 |  |
19 | Not used - set to 255 |  |

Examples

The following table is a copy of the one above (with the master column removed) used as a worksheet for a slave.

Byte | Slave RTU | Example value
0 | 1 | 1
1 | DevAddr | 3
2 | CharProfile | 0 (8,1,N)
3 | BaudRate | 5 (9600)
4 |  | Not used - set to 255
5 |  | Not used - set to 255
6 |  | Not used - set to 255
7 |  | Not used - set to 255
8 |  | Not used - set to 255
9 | MessageGap | 10 (x10mS = 100mS)
10 | QryTimeout | 0 (Not used in slave)
11 | QryAttempts | 0 (Not used in slave)
12 |  | Not used - set to 255
13 |  | Not used - set to 255
14 |  | Not used - set to 255
15 |  | Not used - set to 255
16 |  | Not used - set to 255
17 |  | Not used - set to 255
18 |  | Not used - set to 255
19 |  | Not used - set to 255

In your SPLat program this configuration would look like this:

                NVEM0           ;Start of NVEM data
ModBusCCB:
                NV0Byte         1       ;Protocol
                NV0Byte         3       ;Address
                NV0Byte         0       ;Character = 8,1,N
                NV0Byte         5       ;9600
                NV0Byte         255     ;Not used - set to 255
                NV0Byte         255     ;Not used - set to 255
                NV0Byte         255     ;Not used - set to 255
                NV0Byte         255     ;Not used - set to 255
                NV0Byte         255     ;Not used - set to 255
                NV0Byte         10      ;Message gap 100mS
                NV0Byte         100     ;Query timeout 1S
                NV0Byte         3       ;Query attempts
                NV0Byte         255,255,255,255,255,255,255,255 ;Not used - set to 255


ModBus: Resource mapping

The ModBus protocol was originally devised for use with Modicon PLCs. Modicon uses an address allocation scheme whereby each resource type (i.e. inputs, memory, outputs etc) is allocated its own address range. Addresses are in decimal, they are 1-based and the ten thousands digit denotes the resource type. Despite the fact that Modicon addresses are 1-based, meaning the first of anything is number 1 rather than number 0, in ModBus messages (datagrams) addresses are 0-based. Also, the ten thousands digit (the resource type designation) is not transmitted on ModBus. Instead, ModBus uses different message types (functions) to specify different types of resources.

The following table summarizes the Modicon style resource addressing and how it maps into SPLat resources. You will find this most useful if a device you are using to communicate with SPLat is documented using predominantly Modicon terminology. (The subsequent table couches the same information in more modern computer terms).

As an example, any Modicon reference that has a 0 as its ten thousands digit denotes a "coil". This is called a "0xxxx reference" in Modicon terminology. In Modicon 0xxxx can mean an internal "coil" or an external coil. An internal coil is what we call a semaphore in SPLat, i.e. a 1-bit internal variable. An external coil is what we call an output. To allow you to select between semaphores and outputs at the SPLat end, we assign (map) one block of Modicon "coil" addresses to semaphores and another block to outputs.

Note: If you are using a ModBus compatible product other than Modicon brand (now owned by Schneider Automation), it is very likely that the terminology used is not the same as Modicon. You will have to analyze the manufacturer's documentation to determine their terminology. The best and safest clue is the ModBus function (message) types.

Modicon reference | SPLat resource | ModBus function | Description/clarification
00001 to 01000 | Semaphores 0 to 0999 counting from RAM0, bit0 | 1: Read coil(s); 5: Force coil; 15: Force coils | ModBus "coil" references map to SPLat digital outputs or semaphores depending on the address supplied by the master. Semaphores are counted from the start of RAM.
01001 to 02000 | Digital outputs 0 to 0999; We suggest you do not use this, because your SPLat program will not know what's going on if an output is modified directly from an external ModBus device.
40001 to 41000 | RAM 0 to 0999 in Word mode. | 3: Read Hregs; 6: Preset Hreg; 16: Preset Hregs | Each word in ModBus maps to two bytes in SPLat. In ModBus the most significant byte appears first (Bigendian). In SPLat the most significant byte is the lowest address.
41001 to 42000 | RAM 0 to 0999 in Byte mode |  | Each word in ModBus maps to 1 byte in SPLat. The most significant byte of the ModBus word is forced to zero or discarded.
42001 to 42040 | OBLCD readout | 3: Read Hregs | New in dialect 21 Firmware in standard products. Returns the ASCII character content of the OnBoard LCD display buffer, 2 bytes per word. More ...
10001 to 11000 | Input 0000 to 0999, debounced | 2: Read inputs | Master can poll SPLat for debounced inputs

The 4xxxx references access what Modicon calls Holding Registers. This is a fairly open-ended resource type in Modicon, and is the one that most other vendors use to fulfill their specific needs. You will for example be most likely to see the speed control register of a Variable Speed Drive mapped as a 4xxxx reference. Holding registers in the Modicon world are 16-bits long, corresponding to 2 bytes in SPLat. Because of that we have defined word mode and byte mode mappings. Word mode transfers 2 bytes per holding register. Byte mode transfers 1 byte per holding register. Byte mode wastes message capacity (bandwidth) but lets you read or set individual bytes in SPLat without involving the adjacent byte.

The next table contains the same basic information but re-cast into the actual addresses appearing in the ModBus messages. These addresses are 1 less than the 4 last digits of Modicon reference, because addresses on ModBus are 0-based. We have given both the decimal and the hex versions.

SPLat resource | Decimal |  |  | Hex |  | ModBus Function
 | From | To | From |  | To
Semaphores 0 to 0999 counting from RAM0, bit0 | 0000 | 0999 |  | 0000 | 03E7 | 1: Read coil(s); 5: Force coil; 15: Force coils
Digital outputs 0 to 0999 | 1000 | 1999 |  | 03E8 | 07CF
RAM 0 to 0999 in Word mode. | 0000 | 0999 |  | 0000 | 03E7 | 3: Read Hregs; 6: Preset Hreg; 16: Preset Hregs
RAM 0 to 0999 in Byte mode | 1000 | 1999 |  | 03E8 | 07CF
OBLCD display buffer (Dialect >= 21). Transferred 2 bytes per word. More ... | 2000 | 2039 |  | 07D0 | 07F7 | 3: Read Hregs
Inputs | 0000 | 0999 |  | 0000 | 03E7 | 2: Read inputs


ModBus: Supported functions

The following table lists the ModBus functions supported in SPLat, with a brief description of the function. This is not meant as a detailed treatment of ModBus; you should refer to the published specifications online at places like modbus.org. The hotlinks in the M/S column take you to the corresponding ModBus master script command (described later).

Function | Name | M/S | Modicon ref | Description
1 | Read Coil(s) | M S | 0xxxx | Reads out one or more contiguous coils (external outputs or internal 1-bit variables, i.e. semaphores)
2 | Read inputs | M S | 1xxxx | Reads one or more contiguous inputs. In a slave SPLat accesses the debounced inputs.
3 | Read holding registers | M S | 4xxxx | Reads one or more contiguous Modicon type 16-bit holding registers. In a SPLat slave the addresses are mapped to RAM (data memory).
4 | Read input registers | M | 3xxxx | Reads one or more contiguous Modicon type 16-bit input registers. Some 3rd party products map internal values to these registers. Not supported in SPLat slave.
5 | Force single coil | M S | 0xxxx | Forces a single coil (Modicon: external output or internal 1-bit variable, i.e. semaphore). Force external outputs with caution! SPLat slave allows selective write protection.
6 | Preset single holding register. | M S | 4xxxx | Forces a value into a single Modicon type 16-bit holding register. In a SPLat slave the addresses are mapped to RAM (data memory).
15 | Force multiple coils | M S | 0xxxx | Forces one or more contiguous coils (external outputs or internal 1-bit variables, i.e. semaphores). Force external outputs with caution!
16 | Preset multiple holding registers. | M S | 4xxxx | Forces data values into one or more Modicon type 16-bit contiguous holding registers. In a SPLat slave the addresses are mapped to RAM (data memory).


ModBus: Slave configuration

Configuring a SPLat as a ModBus slave is very simple. You simply need to set up a Communications Control Block (CCB) with the required parameters and invoke it with a ComSetCCB instruction.

The CCB contains the address of the slave, and is frozen at design time. If you need the address to be set dynamically, say from user input, you can use the ComDevAddr instruction. The ComDevAddr instruction must be executed after the ComSetCCB instruction.


ModBus: Master configuration

To set up a SPLat to be a ModBus master you need to do two things:

  1. Configure a Communications Control Block.
  2. Create a control script.

A ModBus master control script is stored in NVEM page 0 memory. You can think of the script as being a simple program in a special purpose language. Most script commands specify a ModBus message to be sent, a slave address, its data content and what is to happen to any data returned by the slave. A small number of script commands perform housekeeping actions.

While control scripts may at first appear rather complicated, they do in fact make life easier. Without control scripts you would have to write a separate task to manage communications. With a control script you essentially specify the messages you need and the system takes care of the rest. Any remaining complexity stems from the nature of ModBus and the need to map out the relationships between various items in master and slave(s).

The full repertoire of ModBus script commands is listed in the programming reference.

Previous Topic

Next Topic

In This Section

ModBus master scripts: A worked example


ModBus master scripts: A worked example

As an example, let's go through the steps to set up the connections illustrated in the above diagram. The SPLat is to be the master and the VSD is to be the slave.

Step 1: Determine resource addresses and data types

We have already determined the addresses in SPLat, as they are given in the diagram. Stated verbally "we want the value in SPLat's RAM location 198 to control the speed of the VSD, and we want the VSD's status flag to be available as semaphore 56". Hence RAM 198 must be written from the SPLat to the VSD, and semaphore 56 must be read from the VSD.

In our example we need to establish the addresses within the VSD. That information must come from the VSD documentation.

Let's say that the VSD manual gives the speed register address as 41234 and the status flag as 00025. These addresses tell us that the speed register is mapped as a holding register (4xxxx) and the status flag as an internal coil (0xxxx). By implication the VSD maker is using Modicon references. These are presumably 1-based addresses (a thoughtful vendor will state that quite explicitly somewhere in their documentation)

We can now make up the following chart:

Variable | VSD address | ModBus address | SPLat address | Transfer type | Direction
VSD speed | 41234 | 1233 | RAM 198 | Holding reg | SPLat to VSD
VSD status | 00025 | 24 | Sem 56 = Byte 7 bit 0 | Coil | VSD to SPLat

Step 2: Decide what ModBus messages to use

No equipment maker supports every ModBus function type in every product. You therefore need to compare the supported function lists for SPLat and for the other device and find appropriate functions supported in both. This is why we have provided some redundancy in the functions we support, to improve the odds of you finding suitable matches.

In the current example the choices will be either function 6 (preset single holding register) or 16 (preset multiple holding registers) for speed, and function 1 (read coil(s)) for the status. Assuming the VSD supports 6, we select that as the simplest of the two.

Step 3: Decide on word mode or byte mode for register transfers.

This is an important point, as it concerns matching the 8-bit world of SPLat to the 16-bit world of ModBus. In the current example we are sending a single byte variable to a 16-bit register, so byte mode is the best choice (it will force the high order byte to 0 automatically).

Step 4: Determine the slave address

Every ModBus device other than the master must have an address. You must consult the VSD documentation to find out how to set its address. Valid addresses are 1 to 247. Assume we set the VSD to 123. The VSD documentation will tell you how to do this (probably via DIP switches or a front panel setting)

Step 5: Write the control script

Once we are this far the hard part is done. The decisions are made and we simply have to encode them into some numbers.

We will write a script that simple runs indefinitely, looping back on itself. That means the SPLat master will generate continuous update messages and keep the VSD and its own memory in step. We call this "continuous and asynchronous", because the communications updates take place all the time, and without regard to anything else going on in the SPLat program or the VSD. It is the simplest thing to do. The downside is that there will be variable delays between the SPLat say changing the speed in RAM 198 and the VSD receiving the changed value. You can make "one shot, synchronous" scripts as well, but it is more difficult.

The script will have to specify two ModBus functions, a function 6 for the speed and a function 1 for the status. The hot links in the previous sentence are to the relevant script commands.

The function 6 script command requires: Function number (6), SlaveAddr (123), RegAddr16 (1233), SPAddr16 (56) and SPMode (0). Hence, the script entry for the function 6 will be

      NV0Byte     6,123,#1233,#56,0

This entry will eventually be placed in the NVEM0 section of your program. The # characters signal a 16-bit entry.

The function 1 script command requires: Function number (1), SlaveAddr (123), CoilAddr16 (24), ItemCount(1) and SPAddr16 (198). Hence, the script entry for the function 1 will be

      NV0Byte     1,123,#24,1,#198

In order to make this script run continuously we need a GoTo script command and a label to target. Putting it all together we get:

      NVEM0                          ;Start of NVEM page 0 data.

;This is my very first ModBus script!

ModBusScript:                        ;A target label for the GoTo

      NV0Byte     6,123,#1233,#56,0  ;Function 6, set the speed

      NV0Byte     1,123,#24,1,#198   ;Function 1, read the status

      NV0Byte     128                ;GoTo opcode: Loop forever

      NV0Ptr      ModBusScript       ;GoTo target

Be very careful to get the # in all the arguments that are specified as 16-bits in the script definitions. Double check! A mistake here could be very hard to debug.

Step 6: Set up the Communications Control Block (CCB)

Before the SPLat can communicate in ModBus, it needs have the correct communications parameters set up. This is done through the Communications Control Block (CCB). This is documented elsewhere. To set up a CCB you need to study the documentation for all the devices in your system and settle on a set of parameters that will work for them all and also with the communications channel (be it a direct wire hookup or a laser bounced off the moon).

The CCB is stored in NVEM page 0 along with the ModBus command script. Here is a typical CCB for a ModBus master:

ModBus_CCB:     ;Communications Control Block for ModBus master

        NV0Byte         2       ;Protocol = ModBus RTU Master

        NV0Byte         0       ;Device address (N/A)

        NV0Byte         0       ;Character profile (8,N,1)

        NV0Byte         7       ;Baudrate = 38400

        NV0Byte         255     ;Not used - set to 255

        NV0Byte         255     ;Not used - set to 255

        NV0Byte         255     ;Not used - set to 255

        NV0Byte         255     ;Not used - set to 255

        NV0Byte         255     ;Not used - set to 255

        NV0Byte         5       ;Message gap x 10mS

        NV0Byte         15      ;Query timeout x 10mS

        NV0Byte         3       ;Query attempts before failure

        NV0Byte         255,255,255,255,255,255,255,255,     ;Not used - set to 255

There is a ModBus CCB work sheet here.

Step 7: Invoke!

All that is left to do is to have your SPLat program execute the right instructions to invoke the CCB and to start the control script running. This would normally be done near the start of your program, during initialization. The instructions for this are:

;Get the ModBus communications going.

        ComSetCCB       ModBus_CCB      ;Point processor at CCB for ModBus

        ComRunScript    ModBusScript    ;Start the comms control script

Once those two instructions are executed, the script will start running and simply do its job, fully automatically, in the background. Note that for the first 10 seconds after startup SPLat cannot switch protocols, so the ComSetCCB may cause a delay (depending on how you use it).


Example: Simple PC<==>SPLat comms using ModBus

You will find a collection of program examples on our website in the File Resources area. This contains a folder \ModBus. This folder contains SPLat code and Visual Basic 6 (VB6) code for a simple example application that communicates between a SPLat controller and a PC. The programs (especially the VB6 code) have been kept as simple as possible to make them easy to follow. There is extensive documentation within the programs themselves.

There is a more elaborate example available under SPiceX.

For an alternative, very easy way of using a PC as a user interface to a SPLat, or to perform data logging to a PC, see SimpleHMI.


User programmable protocol

The "User programmable protocol" is not actually a pre-implemented protocol; it is a set of instructions that gives you sufficient flexibility to program SPLat to communicate with a wide range of 3rd party products. We describe these collectively as "ASCII protocols", because most such products have a command/data message format that use human-readable ASCII characters. You can also program protocols that use non-ASCII codes.

A typical ASCII protocol consists of lines of text that will appear readable on a terminal emulator such as HyperTerm or RealTerm or the terminal function built into SPLat/PC. Each line (terminated in a carriage return and/or line feed character), will typically consist of:

  • A start character, often something like a colon or an asterisk
  • One or more command characters, say 'M' for move
  • One or more numeric argument as a string of digits, leading plus or minus sign and embedded decimal point. More often than not the multiple numeric argument are separated by a defined character such as space or comma, but some devices place them in fixed width fields with no separator (for example some Mettler/Toledo weigh scales when transmitting Indicated and tare weights).
  • Terminating carriage return/line feed.

For example, a hypothetical motion controller might have commands to Move Absolute (MA) or Move Relative (MR), Set Acceleration (SA) and Set Maximum (speed). Typical commands would be (don't struggle to fully understand this, you are better off concentrating on your own real application!):

!SM25

!SA0.15

!MA12345

!MR-983

In some devices the start character, '!' above, is sufficient, so the end of line carriage return is not required. Were that the case, the following would be equivalent to what's above:

!SM25!SA0.15!MA12345!MR-983

Previous Topic

Next Topic

In This Chapter

User programmable protocol: Communication Control Block

User programmable protocol: Sending out messages

User programmable protocol: Receiving messages


User programmable protocol: Communication Control Block

Since this was written we have introduced Hash commands. The Open_Serial hash command make this much simpler to do.

User programmable protocol is selected and configured via a suitable Communications Control Block (CCB). The following table summarizes the various parameters in the CCB. Entries are hyper-linked to detailed descriptions. If you are using a User programmable protocol you must set up a CCB with the specified parameters and then invoke it in your program with a ComSetCCB instruction.

Byte | Function | Example value
0 | 3 | 3
1 |  | Not used - set to 255
2 | CharProfile | 0 (8,1,N)
3 | BaudRate | 5 (9600)
4 |  | Not used - set to 255
5 |  | Not used - set to 255
6 |  | Not used - set to 255
7 |  | Not used - set to 255
8 |  | Not used - set to 255
9 |  | Not used - set to 255
10 |  | Not used - set to 255
11 |  | Not used - set to 255
12 |  | Not used - set to 255
13 |  | Not used - set to 255
14 |  | Not used - set to 255
15 |  | Not used - set to 255
16 |  | Not used - set to 255
17 |  | Not used - set to 255
18 |  | Not used - set to 255
19 |  | Not used - set to 255

Note there are a lot of unused settings! It is safest to set them all to 255 in case we ever invoke any of them in future Firmware revisions.


User programmable protocol: Sending out messages

Interacting with an external device via the serial port will frequently entail sending out messages or commands to that device. You would do this by sending the individual "components" of a message one at a time. The instructions you need are the ones that start with ComTx_ or ii under Communications instructions. The trick with the ii instructions is to use destination address 251, which directs the output of the instruction to the serial port (providing you have initialised it to User Programmable Protocol).

Example:

The following program will send out ASCII numeric and text strings terminated in carriage return/line feed. You can view the result using the terminal function built into SPLat/PC. Once the program is running (you should see a flashing LED on the MS120 front panel, click the SIO button on the SPLat/PC Module window. This will open up the terminal window. In the settings tab make sure the Comms parameters are 38400 for Baud and N,8,1 for Character (this is the same as on this page. Once the settings are correct, click the Terminal tab, and make sure the ASCII button is depressed. When you start pressing buttons on the MS120 you should see the data from the MS120 displayed on the black terminal display. If you now select the Hex button, you can see the same data in hex.

Here's the code: (Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;Example of sending ASCII stuff out the serial port. Designed for MS120, easily adapted to MMi202.
;Input button 12 increments a counter and sends its value
;Input button 13 decrements the counter and sends its value
;Input button 14 Sends a canned message
;Input button 16 deliberately crashes the program so SPLat/PC can regain control (connect).
;There is one MultiTrack task per function. Another taks flashes a LED as a run indicator

        OBLCD_Dim       1,8,2                   ;Set up LCD backlight for dimming

#       Open_Serial     User(38400, 8, N)       ;Start up the serial port in "user" protocol. This will delay 10 seonds

        LaunchTask      UpCount

        LaunchTask      DnCount

        LaunchTask      CannedMsg

        LaunchTask      HeartBeat       ;Run indicator

        LaunchTask      CrashMe         ;Suicide task

        RunTasksForever

fCount  defFLOAT                        ;The up/down counter

UpCount:

        WaitOnK         12              ;Up button

        fRecallW        fCount

        fINC

        fStore          fCount

        GoSub           OutputCounter

        GoTo            UpCount

DnCount:

        WaitOnK         13              ;Dn button

        fRecallW        fCount

        fDEC

        fStore          fCount

        GoSub           OutputCounter

        GoTo            DnCount

CannedMsg:

        WaitOnK         14

        iiPrintText     251,"Hello World",'0D,'0A         ;251 is the serial port

        GoTo            CannedMsg

HeartBeat:

        On              8       ;The LED under the kill button

        Pause           2

        Off             8

        Pause           20

        GoTo            HeartBeat

CrashMe: ;Suicide

        WaitOnK         16

Die     GoSub           Die     ;Program wil restart at once, but with 10S delay before user comms starts up.

;Serial output counter

OutputCounter:

        fRecallW        fCount

        iifPrintWVW     251,5,0         ;251 is the serial port

        iiPrintText     251,'0D,'0A     ;Carriage return/line feed

        Return


User programmable protocol: Receiving messages

To be able to effectively program a SPLat to receive messages via a user programmable protocol, you first need to be very accurately informed about what the external device sends out. You should not only study the documentation on the device, but preferably also connect it to a terminal emulator and actually see the messages displayed on your screen. The terminal function built into SPLat/PC is useful for doing this. It can display received data in ASCII and hex, and allows you to easily generate outgoing messages with a mixture of printable and non-printing characters. It also allows multiple outgoing messages to be stored and sent with a button click. There are also a number of terminal emulators available online, such as RealTerm. Ideally you should try and fully interact with the device manually, and understand exactly what's happening, before trying to code the interaction into SPLat.

The relevant instructions are those that start with ComRx_ under communications instructions. These allow you to look out for specific "signal" characters, trim out filler characters (like space), and extract decimal or hexadecimal numbers from the receive buffer.

SPLat/PC includes a serial communications simulation window, which is very useful when you are at the stage of single-stepping your program in the SPLat/PC simulator. You can also use the terminal function in SPLat/PC to interact with a program running in the SPLat controller.

Example 1.

This example presumes an external instrument that transmits periodic readings in ASCII. The reading format is simple a number followed by carriage return. A typical message would look like this:

123.45<CR>

when <CR> represents the carriage return character, or '0D. At this point I advise you to go to the SPLat/PC terminal. In the Module window click SIO, in the window that appears select the Terminal tab. This gives you the terminal emulator. The bottom half is dedicated to sending stuff to a connected SPLat controller (or anything else you care to connect serially!). Type in what I typed in the image below, and take special note of the blue help text and what appears in the very bottom section. Play around until you are comfortable with composing messages with printing and non-printing characters. (a letter A is printing, as is a space, while a carriage return is not)

The nice thing about this terminal function is that you can type in a message once and click its Send button many times.

Once you are comfortable with how to send a message to the SPLat, you can proceed with the program below. Translate and send the program to your board, and run it, make sure the Terminal setting are as shown in the lower lefthand corner of the screen shot above. Now try sending numbers terminated in <CR>. (Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;Example of receiving ASCII numbers into the serial port. Designed for MS120, easily adapted to MMi202.
;There is one MultiTrack task per function.

        OBLCD_Dim       1,8,2                   ;Set up LCD backlight for dimming

#       Open_Serial     User(38400, 8, N)       ;Start up the serial port in "user" protocol. This will delay 10 seconds

        LaunchTask      ReceiveNumbers

        LaunchTask      HeartBeat       ;Run indicator

        LaunchTask      CrashMe         ;Suicide task

        RunTasksForever

fRxNumb  defFLOAT                        ;The received

ReceiveNumbers:

;Wait until there is a CR in the receive buffer

        YieldTask

        LoadX           '0D             ;CR

        ComRx_FindXInBuf                ;X become 255 if NOT found

        IncX

        GoIfZ           ReceiveNumbers

;Now I have a CR, I know there has to be a number

        ComRx_fGetNum   255             ;Fetch the number

        ComRx_ReadOne                   ;Gobble the CR

        OBLCD_SetCur    0,0

        OBLCD_fDispW    9,3             ;Display the number

        fStore          fRxNumb         ;Save it

        GoTo            ReceiveNumbers

CrashMe: ;Suicide

        WaitOnK         16

Die     GoSub           Die     ;Program wil restart at once, but with 10S delay before user comms starts up.

HeartBeat:

        On              8       ;The LED under the kill button

        Pause           2

        Off             8

        Pause           20

        GoTo            HeartBeat

Example 2.

This example is more elaborate. A hypothetical device, connected to the SPLat, can send 4 different messages: These are:

    ON<CR>

    OFF<CR>

    W=123.45<CR>

    H=78.9<CR>

The first two should result in an output (front panel LED) turning on or off. The last two start with W= or H= and include a variable numeric value, which is to be displayed. The program uses the ComRx_StrFind instruction to identify received text strings via a table of expected strings. ComRx_StrFind is a very powerful instruction, and you should spend a few minutes reading up on it.

;Example of receiving ASCII commands into the serial port. Designed for MS120, easily adapted to MMi202.

        OBLCD_Dim       1,8,2                   ;Set up LCD backlight for dimming

#       Open_Serial     User(38400, 8, N)       ;Start up the serial port in "user" protocol. This will delay 10 seoonds

        LaunchTask      ReceiveStuff

        LaunchTask      HeartBeat       ;Run indicator

        LaunchTask      CrashMe         ;Suicide task

        RunTasksForever

ReceiveStuff:

;Wait until there is a CR in the receive buffer, signalling a complete command.

        YieldTask

        LoadX           '0D             ;CR

        ComRx_FindXInBuf                ;X = 255 if NOT found

        GoIfXEQ         255,ReceiveStuff

;Now I have a CR, I know there has to be something else.

        NVSetPtr        Pointers        ;Point to table of string pointers

        ComRx_StrFind   0               ;Look for a match with the strings stored in NVVEM

        Push

        iiPrintText     251,"Got one "  ;Diagnostic

        iiHexPrintX     251             ;Diagnostic

        iiPrintText     251,'0D,'0A     ;Diagnostic

        Branch                          ;Test X and branch accordingly

        Target          Nothing

        Target          doON

        Target          doOFF

        Target          doW

        Target          doH

DoON:   On              11               ;Tuirn on a LED

        GoTo            ReceiveStuff

DoOFF:  OFF             11               ;Tuirn off a LED

        GoTo            ReceiveStuff

DoW:

        ComRx_fGetNum   255             ;Get the number

        OBLCD_SetCur    0,0

        OBLCD_Text      "W="

        OBLCD_fDispW    9,3

        ComRx_ReadOne                   ;Flush the terminating CR

        GoTo            ReceiveStuff

DoH:

        ComRx_fGetNum   255             ;Get the number

        OBLCD_SetCur    1,0

        OBLCD_Text      "H="

        OBLCD_fDispW    9,3

        ComRx_ReadOne                   ;Flush the terminating CR

        GoTo            ReceiveStuff

Nothing:   ;If no match, flush one character to (hopefully!) expose something we can recognise

        ComRx_ReadOne           ;Waste one character out of RX buffer

        GoTo            ReceiveStuff

CrashMe: ;Suicide

        WaitOnK         16

Die     GoSub           Die     ;Program will restart at once, but with 10S delay before user comms starts up.

HeartBeat:

        On              8       ;The LED under the kill button

        Pause           2

        Off             8

        Pause           20

        GoTo            HeartBeat

**********************************************************************

       NVEM0

;NVEM tables

;List of expected "commands". Note they must all terminate in 0

strON:  NV0Byte         "ON",'0D,0

strOFF: NV0Byte         "OFF",'0D,0

strW:   NV0Byte         "W=",0

strH:   NV0Byte         "H=",0

;Pointers into the list of commands

Pointers:

        NV0Ptr          strON

        NV0Ptr          strOFF

        NV0Ptr          strW

        NV0Ptr          strH

        NV0Byte         255     **** Essential terminator ****

Experiment with this program, and getting it receiving and acting on inputs from the Terminal window.

I have left in a bit of diagnostic code, which prints out the index number of the string found in the table. Its output appears on the Terminal screen.

Note the code at the program line Nothing: If a received string is not recognised, we throw away one receive character from the head of the buffer. This ensures that, should anything go wrong, the program will eventually recover so long as it is receiving <CR> characters.

Exercises.

  1. Remove the ComRx_ReadOne at Nothing:, then try sending a bad message like OX instead of ON. What happens? Restore the ComRx_ReadOne and try again.
  2. Why have I got <CR> (or '0D) terminators on the first two strings in the NVEM table, but not for the second 2? Hint: Why do I have ComRx_ReadOne instructions in the segments doW; and doH:?
  3. Experiment with the ComRx_StrFind argument.
  4. Merge this example with the one under "User programmable protocol: Sending out messages" to make a program that both receives and decodes stuff and sends stuff out. Get creative, so what is sent out is in response to something received.


RS232 pinouts: a primer

RS232 pinout designations can be extremely confusing! The origin of RS232 was as an interface between (old fashioned) computer terminals and modems. Modems provide communications, and are referred to a Data Communication Equipment (DCE). Terminals are, well, terminals, and are referred to as Data Terminal Equipment (DTE).

Traditionally the modem was regarded as a 'service', a bit like a power utility, so the connector on a modem is a female, just like a power outlet.

The signal names are all derived from this mental model. Hence:

Transmit Data (TxD) is data being transmitted via the modem. It comes out of the terminal (DTE) and goes in to the modem (DCE)

Receive Data (RxD) is data being received by the modem. It comes out of the DCE and goes in to the DTE.

Request To Send (RTS) is something the DTE must do. Hence RTS is an output from the DTE and an input to the DCE.

Clear To Send (CTS) is something the DCE may grant. Hence CTS is an output from the DCE and an input to the DTE.

Data Carrier Detect (DCD or just CD) is generated by the modem when it has an incoming signal (carrier) from the 'phone line. Hence it is an output from the DCE and an input to the DTE.

In many systems, RTS and CTS form the basis of a hardware handshaking system. Imagine a modem that cannot transmit and receive data at the same time (such was the case at least in the "bad old days" of 1200 baud modems, before the Internet fueled the development of 14.4K, 28K 34K and 56K modems). If that's hard to imagine, try instead thinking of a pair of single frequency walkie talkies where you have to press a button to transmit. This is called a half duplex system because you can only communicate in one direction at a time. RTS and CTS are then needed to help sort out when the terminal is allowed to transmit data. In such a case, when the DTE needs to transmit it "raises" RTS. The DCE then turns on its transmitter, which may take some finite time to get up and running, and then "raises" CTS. The DTE sees CTS and sends its data, and when done "drops" RTS. Finally the DCE drops CTS.

The DCD signal alerts the DTE to an incoming carrier. In some systems this may presage some incoming data, and should certainly bar the DTE from attempting to send anything in a half-duplex system.

Two other common signals are static ready signals. These are:

Data Set Ready (DSR). This is an output on the DCE and input on the DTE. More often than not DSR is simply a positive voltage (+5V or +12V) brought out of the modem via a current limiting resistor, so it comes on when power is on.

Data Terminal Ready (DTR). This is an output on the DTE and input on the DCE. Like DSR, DTR is usually just +5V or +12V fed via a current limiting resistor.

Finally there is Gnd, which is simply the common return for all signals.

When RS232 was first introduced by IBM, there were many other signals needed (forget about learning that detail!) and a 25 way connector was used. When IBM introduced the PS2 PC, they saw fit to change the standard they first invented and went to a 9 way connector. And in the process they changed the pin numbers all around, including actually reversing RxD and TxD pin number assignments. Naturally this has been a source of perpetual confusion ever since!

Where does all this leave us with SPLat?

When we designed SPLat we decided to use a pinout that would be as easy as possible to connect to a PC. Now, the connector on a PC is a male 9-way (called a DB9P plug) and is wired as a DTE (terminal device). Therefore, we made the SPLat "look like" a DCE to the extent we could without making it cost more. Hence a SPLat has a female connector (DB9S socket). SPLat only needs RxD (an output from a DCE) and TxD (an input to a DCE). It doesn't need RTS in, and has no interest in turning CTS on and off. Instead we generate CTS and DSR signals as permanent high levels, just to be "nice". All this means we keep the price down and you only need a plain vanilla DB9 cable to connect a SPLat to your computer.

On first generation SPLat boards we saved some cost by not having an onboard negative voltage supply (RS232 needs positive and negative voltages on the interface) Instead, we rely on the computer "giving" us a negative level on the RTS line. SPLat/PC ensures that RTS is low (negative). By the time we started designing 2nd generation SPLat boards, we had customers using SPLats over modem links and data radio links, so we incorporated a genuine onboard negative voltage source to make it easier to interface to a modem.

Here are the most important signals in summary form:

Name | Direction | DB9 | DB25
TxD | DTE --> DCE | 3 | 2
RxD | DTE <-- DCE | 2 | 3
RTS | DTE --> DCE | 7 | 4
CTS | DTE <-- DCE | 8 | 5
DTR | DTE --> DCE | 4 | 20
DSR | DTE <-- DCE | 6 | 6
DCD | DTE <-- DCE | 1 | 8
Gnd |  | 5 | 7


Tutorial: Debugging SPLatWare using serial data

With the advent of a user programmable serial protocol capability, and with the inclusion of a serial terminal function, a very powerful technique has become practicable for debugging SPLat programs. This tutorial will show you some useful techniques using these resources.

Note: Since this tutorial was written we have developed SimpleHMI, an alternative way of interacting with a running SPLat program via the serial port. For certain uses SimpleHMI can offer a better capability.

The serial terminal (this tutorial) is the right choice if you are developing serial communications to fit a 3rd party device such as a motion control board or a weigh-scale, which has its own predetermined message formats (protocol).

As a powerful tool for general program debugging SimpleHMI offers a different set of features. SimpleHMI is also easier to program because it uses hash commands.

This is mainly about debugging techniques. That said, it will equip you for any challenge that involves generating arbitrary serial data out of a SPLat controller.

Target audience

This tutorial is designed to inspire and inform SPLat users who have never had any experience of squirting debug data out of a spare port. If the previous sentence make perfect sense to you, you probably needn't continue. Just study the communications instructions, read up on the terminal function in SPLat/PC, and you are good to go. (That said, you may find a few new ideas in here).

If you have never used any debug tools beyond what SPLat/PC has offered in the past (prior to November 2011), then this could be a very good thing to study.

You probably also need to be developing well-structured programs of some complexity to make this (serial port debugging) worth the effort.

Outcomes (what you will get out of this tutorial)

  • An understanding of how to generate outgoing (transmit) data on the serial port.
  • Some useful tricks for debugging simple and complex programs.

Prerequisites (what you should know before you start):

  • Solid SPLat programming skills. This is not for beginners. You will basically be creating your own debug resources on an ad-hoc basis, so you shouldn't see writing 5 ot 10 lines of extra code here or there as a big burden.
  • Knowledge of the SPLat communications instructions, particularly the ones for transmitting data (those with keywords starting in ii and containing Print (for example iiPrintText and iifPrintWFW).
  • A good working knowledge of MultiTrack.
  • Experience with connecting a SPLat controller to your PC and programming it with SPLat/PC
  • Prior reading of the terminal function in the SPLat/PC documentation.
  • Very useful, if not essential: Skills with Builder, so you can turn debug code on or off at will.
  • Some understanding of NVEM.

Hardware and software requirements

  • A SPLat controller with dialect 28 or later. Check your board's documentation for available firmware updates (reFlash files), if necessary.
  • SPLat/PC Build 146 or later
  • Power supply
  • Connection cable to SPLat/PC.
  • Some easy way of turning input on and off (a MMi202, MS120 or HD8 is ideal, as they have push buttons. A CC18 with a CCBB breakout board is also good)

________________________________________________

Related topics

SimpleHMI Serial communication

Previous Topic

Next Topic

In This Chapter

Overview

Setting up the serial port

Serial debugging: Sign posts

Serial debugging: State tracing

Serial debugging: Getting back control (with SPLat/PC)

Serial debugging: Standalone SimpleHMI


Overview

The basic principle of serial debugging is this: At strategic points in your program you insert a small bit of special debug code, which sends some data out through the serial port for display on the terminal. Typically this debug data will be in human-readable ASCII.

That's it!

This is a very simple idea, but quite powerful. No programming/debugging tool, like SPLat/PC, is capable of having features targeted at every situation that could arise while developing a complex program. Being able to write your own tool, so to speak, and have it run in the SPLat is very powerful.

The rest of this tutorial will basically show you a number of ideas based on this theme. Don't see these as being the full repertoire of what can be done. Rather, consider them as a starting point for your own creativity.


Setting up the serial port

Before you can send data out of the serial port, you have to initialise it. This is done using hash command to open the serial port with the User protocol. The following code will get the serial port going:

;------ Initialisation code -----------------
#       Open_Serial User(38400,8,N,1)
        iiPrintText     251,"Hello World",'0D,'0A
        ......	continue the program

The "Hello World" is a bit of decoration, but also signals that the serial port is initialised. It ends with '0D,'0A (carriage return/ line feed) to force the terminal display to a new line. 251 is the RAM "address" that directs output to the serial port rather than to RAM. Your program would be a bit more readable if you instead used a symbolic name, and somewhere defined the symbol as EQU 251.

Once the Open_Serial hash command has executed, there will be a delay until 10 seconds after startup before the serial port can actually be used. If you attempt to use it before then, your program will block (lock up) until the time expires. You can use the ComTestStartTimer instruction to work around this and do something useful in those 10 seconds, if you feel so inclined. I sometimes use the time to display a splash screen, if I have an LCD, or blink a coded SPLatWare revision number if I have a LED or two available.


Serial debugging: Sign posts

One very useful serial technique is what I call signposts. You simply insert a bit of code at strategic parts of a program to see where it is going. This is particularly useful if it looks like your program is getting lost or never getting to an important part. Maybe your program fails to turn off the fill valve when the vessel is full (hopefully you are bench testing this, so no physical harm is being done!). The program will contain a chain of events that leads, ultimately to the crucial valve being turned off, but for some reason it never gets to the turnoff instruction.

What you do is scatter a number of simple iiPrintText instructions throughout the relevant code. Let's say you simply use

       iiPrintText         251,"A"
        ......
       iiPrintText         251,"B"
        ......
       iiPrintText         251,"C"
        ......
       iiPrintText         251,"D"
        ......

where the dots represent your regular program code. It's really easy to insert those line, by using copy and paste and a simple edit on each sign post. Now you run your program and do to it those things that should lead to the valve turning off, and note which of the sign posts get displayed on the terminal. Say it displays A and B, but never reaches C. It must be getting lost between B and C.

Go to the section of code between B and C and insert several more sign posts, say B1, B2, B3 etc. Run the program again. Soon you will be homing in on the exact point where it is mis-stepping. Maybe you have a GoIfT instead of a GoIfF, or you used a Recall rather than a RecallS. Those kinds of coding typos can be very hard to spot, but with sign posts you can home in on them very quickly.

When you have found the bug, go through and delete the sign posts. They are so easy to create that they are not worth keeping.


Serial debugging: State tracing

State tracing is very similar to sign posts. You could also call it sign posts applied to Event-Action programming (a.k.a Finite State Machines or FSM).

This is not the place to teach you about FSMs. Suffice to say they are the most powerful way known to man of programming control sequences that can take into account the past history of the system. One easy way to get into this technology is with our Tabula programming tool.

If you understand FSMs, you will understand the concept of adding a serial debugging signpost on every state entry, which gets execute just one upon entering the state. You could also add signposts to event code or transition code, but that is more cumbersome. Generally it is quite possible to identify a bug by tracking state entries.

One advantage of numbering states, as distinct from (or maybe in addition to) naming them, is that the signpost display can take up less space on the terminal screen. Tabula supports this idea, in that states (table columns) are always numbered.

Multiple state machines

If I have multiple state machines running under MultiTrack, and cannot debug one in isolation from others, I enclose the state numbers in different types of brackets, so that (3) and [3] are states in different state machines.

Multiple instances of one state machine

If you have multiple instances of a particular state machine in several MultiTrack tasks, it becomes a little more interesting. You then need to generate signposts that includes the task's value of J. as well as the state number. There are two ways to discover a task's J value, using either BranchJ or LaunchTaskX. Both methods require some special initialisation code, plus an array of jndexed bytes to hold the state numbers


Serial debugging: Getting back control (with SPLat/PC)

Once the serial port has been switched to "user protocol" mode, it is impossible for SPLat/PC to command the controller to stop. This means SPLat/PC cannot connect to the controller (referring here to the "classic" SPLat/PC debug functions, which require the SPLatLink protocol.

There are two ways around this:

  1. Power cycle the board. You then have a 10-second window to connect.
  2. Get the board out of user protocol programmatically.

The second methods requires some way of signalling to your program that you want to get back control, and then having your program do something to make it happen. The easy way is to have a dedicated task watching a spare input, and then crash the program with a subroutine that calls itself. Example:

           LaunchTask  KillTask
           ...
           RunTasksForever
           ...

KillTask:  WaitOnK     iKillSwitch

           iiPrintText 251,'0D,'0A,"Terminating with extreme prejudice."

Die:       Gosub Die

This is quick and easy, if somewhat brutal. It will always report a runtime error when you do connect. You still have to connect within 10 seconds.

A more subtle way is to switch back to SPLatLink.

           LaunchTask  GracefullyRetire

           ...

           RunTasksForever

           ...

GracefullyRetire:

           WaitOnK    iRetireSwitch

Retire:

           iiPrintText 251,'0D,'0A,"Retiring gracefully"

#          Open_Serial SPLatLink(9600)

           GoTo       GracefullyRetire

           ...

Which method you use is up to you.


Serial debugging: Standalone SimpleHMI

The terminal functionality is also available in the standalone Windows version of SimpleHMI. Normally that program does not show the Terminal function, because it is designed to be as basic as possible for the Enduser of SimpleHMI. However, you can simply include the switch /T in the command line/shortcut for SimpleHMI standalone and the Terminal tab will be visible. The shortcut target should look something like this:

"C:\Program Files (x86)\SPLat\SimpleHMI_Standalone\SimpleHMI.exe" /T


Tutorial: SimpleHMI programming of SPLat controllers

Date originally written: January 2012, before the advent of the HMI430 controller with integral touch screen. This tutorial is written around an HMI device (Android or PC) external to the SPLat controller. It is still a good starting point for understanding the HMI430.

SPLat controllers can support a Human-Machine Interface (HMI) software client called SimpleHMI. SimpleHMI is available on the following devices:

  • (From 3Q2014) SPLat controllers with integral touch screen, such as HMI430;
  • Your personal computer as a SimpleHMI client built into SPLat/PC;
  • An Android phone or tablet running our SimpleHMI app and connected to a SPLat controller via Bluetooth. Bluetooth adaptors are available for SPLat controllers.. Download SimpleHMI for Android
  • A personal computer as a standalone Windows program. Download

"Big picture" features of SimpleHMI:

  • Extremely simple to program (we called it SimpleHMI for a reason!);
  • All application-specific programming is in the SPLat. The SimpleHMI device (Android or whatever) always runs factory standard code. In the HMI430, there is only one program to write and maintain, unlike competing solutions where a controller (typically PLC) and an HMI device must have separate programs that need to be kept "in sync" with each other;
  • The Windows version is designed for functionality rather than beauty. The more fancy a display gets, the harder it is to program and maintain. That said, it allows clean, logical screen layouts customized for each specific purpose;
  • The Android version supports limited graphics functionality. It is intended to allow Proof of Concept user interface to be implemented quickly and easily, as a pre-cursor to a full Android/iOS app when your product gets into volume production.
  • Highly configurable buttons to trigger actions in the SPLat controller;
  • Ability to display SPLat data on the SimpleHMI screen;
  • A very easily programmed method of getting an end-user to supply configuration data to the SPLat;
  • Built in data logging capability (Windows and Android).

SimpleHMI can reduce the effort required to program an operator interface with multiple configuration settings by as much as 90% compared to the simple character LCD and buttons approach.

Outcomes (what you will get out of this tutorial)

  • You will get started with programming SPLat to utilise the SimpleHMI;
  • You will discover some powerful techniques for debugging SPLat programs;
  • You will gain a good insight into the use of Hash commands.

Prerequisites (what you need before you start)

  • A working knowledge of MultiTrack programming (this all builds on MultiTrack);
  • Good skills with SPLat/PC;
  • Any SPLat controller with language dialect 27 or later, with programming cable, set up and working with SPLat/PC. Ideally the controller will have some visual output indicators and some way of easily turning some inputs on and off;
  • The latest SPLat/PC.

Note: It is possible to fully develop an application using the SimpleHMI built into SPLat/PC, connected via the regular programming cable. A Bluetooth adaptor is needed for connecting to an Android device, and is available on our website. It is easiest to do most of your work using SPLat/PC, then fine tune against the Android device. If you are using a SPLat EC1 you can use a 3rd party JY-MCU Bluetooth adaptor, but make sure the SPLat program uses 9600 baud (many of the examples are coded for 38400 baud).

How long will it take?

The tutorial shouldn't take very long to complete, maybe an hour or two to get the essentials (providing you have the prerequisites in place). After all, we do call it SimpleHMI!

So let's get started!

_________________________________________________________

Related topics

Debugging using serial data and the terminal function in SPLat/PC Serial communications

Previous Topic

Next Topic

In This Chapter

SimpleHMI: What it is, has, and does

SimpleHMI: Getting connected with SPLat/PC

SimpleHMI: Hash commands and functions

SimpleHMI: Android, Bluetooth and Connection

SimpleHMI: Introducing Events

SimpleHMI: Displaying text

SimpleHMI: Cursor control

SimpleHMI: Colours

SimpleHMI: User input

SimpleHMI: Speech output

SimpleHMI example: PIN numbers

SimpleHMI: Programming beyond the hash

SimpleHMI: Getting fancy with cursor positioning and screen sizes

SimpleHMI: The standalone Windows version

SimpleHMI: The logging feature

SimpleHMI: The trace feature

SimpleHMI: Hosts other than SPLat


SimpleHMI: What it is, has, and does

SimpleHMI is a software program available as an Android app and also integrated into SPLat/PC (the programming tool, or IDE, used for programming SPLat controllers). SimpleHMI receives display commands from, and returns Enduser responses back to, a "host", which is normally a SPLat controller.

SimpleHMI has the following resources:

  • A row/column addressable screen to which you can "print" text;
  • A fixed number of buttons (initially 100) that an enduser can touch/click to send a signal to the host. They are numbered 1 to 100;
  • A data entry feature that allows an enduser to enter numeric data to be sent back to the host.

The default screens for SimpleHMI on Windows (left) and Android (right) look like this:

The default appearance may change with future versions. That's not very important, because it is unlikely that any use of SimpleHMI will leave the screen looking like that ... it's just too easy to program your SPLat controller to change the screen.


SimpleHMI: Getting connected with SPLat/PC

The first step in programming your SPLat controller to control SimpleHMI is to get a real SPLat connected to the SimpleHMI function inside SPLat/PC.

Here are the steps (I assume you are already familiar with using SPLat/PC, connecting to SPLat boards, etc. That means you have the right programming cable, that you know which comms port to use, etc.)

  1. Make sure you are running the latest SPLat/PC Build.
  2. Run SPLat/PC and get it connected to your controller. Acid test: Interact with the board via the SPLat/PC I/O window.
  3. Translate and run the program listed below in your controller. After 10 seconds output 0 should start flashing (if you have a LED connected to it!);
  4. On the module window, click SIO.
  5. On the Terminal window, which should appear, click the Settings tab.
  6. Make sure the settings are 7:38400 for Baud and 0:8,N,1 for Character.
  7. Click the SimpleHMI tab.
  8. (Nearly there!) Click the Connect button on the SimpleHMI tab.
  9. Confirm that you get a Hello World message on the SimpleHMI display.

You need to get the required end result of this test before continuing ... this is the proof that everything is working.

Here's the test program.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;Simple test of SimpleHMI. Intended just to show it's working in the PC
HMIPort EQU      251
#       Open_Serial Port(HMIPort) User(38400,8,N)         ;Initialise the serial port
#       HMI Port(HMIPort) ConnectEvent(HMI_Connect)       ;Where to GoSub when the SimpleHMI connects
        LaunchTask      HeartBeat                         ;Show we are alive
        RunTasksForever

;----- HeartBeat task -----

HeartBeat:

        On              0

        Pause           2

        Off             0

        Pause           50

        GoTo            HeartBeat

;---- Event handler for the HMI Connected event -------

HMI_Connect:

#       HMI Cursor(5,10) Print("Hello World") ;Display a message at row 5, column 10

        Return

Note: HMIPort (251) is the address of the controller's programming port when used by your application. In the EC1 and future 32-bit products there can be more than one program-accessible serial port, which will have other addresses. The references here to HMIPort are redundant - if omitted the default is still port 251.


SimpleHMI: Hash commands and functions

In the program on the previous page you will notice that virtually everything to do with the SimpleHMI is in program lines starting with a #. If this is unfamiliar to you, now is the time to read up on Hash Commands (which we originally developed for the SimpleHMI project, but will carry over to many other areas).

  • The first hash line
#       Open_Serial User(38400,8,N)         ;Initialise the serial port

initialises the serial port to the right settings to control the SimpleHMI. This replaces the older ComSetCCB plus a Communication Control Block in NVEM. What happens is that SPLat/PC automatically generates the code and NVEM table, under the hood.

  • The second hash line
#       HMI ConnectEvent(HMI_Connect)       ;Where to GoSub when the SimpleHMI connects

prepares the ground so that when the SimpleHMI signals that it has connected, the subroutine located at HMI_Connect will be called.

  • The third hash line
#       HMI Cursor(5,10) Print("Hello World") ;Display a message at row 5, column 10

is in the HMI_Connect event handler subroutine. It positions the text display cursor on the SimpleHMI, then displays a text message.

Technically the keywords Open_Serial and HMI are hash commands, while User(), ConnectEvent(), and Cursor() are hash functions.

The hash commands and functions help make the SimpleHMI very simple to program.


SimpleHMI: Android, Bluetooth and Connection

This page belongs logically much later in the tutorial, but makes a very important point that you need to be aware of.

With Android and Bluetooth, there is no certainty as to when a device is actually connected or not. Put another way, you can walk up to a machine fitted for SimpleHMI via Bluetooth at any time and connect to it (if you know the pairing password).

When SimpleHMI gets connected via Bluetooth, it automatically sends a Connected event to the SPLat. This is used in your program to redraw the SimpleHMI home screen.

If you are programming for SimpleHMI your program must be able to handle a Connected event at absolutely any time without doing anything dangerous.

The easy way to handle the Connected event is simply to paint the home screen. You must either design your system so this is always safe, or keep track of the state of the system and select which screen to paint accordingly (probably using a Branch instruction).

SimpleHMI in Android sends Connected events when it first connects and when it reconnects after being re-launched. In Android reverting to a "running" app actually re-launches it. This includes when the screen is turned on after being off.


SimpleHMI: Introducing Events

The first sample program above already handles one event, namely the Connect event. What happens is that when you click the Connect button on the SPLat/PC SimpleHMI screen, a special message is sent to the SPLat via the serial port. The SPLat detects this and automatically does a GoSub to your event handler. The event handler is simply a subroutine that does whatever is required in response to that event. It must exit via a Return instruction.

This event-based mechanism is carried through the whole set of HMI hash functions. Events are also generated by button clicks and by user data entry.

Events provide a lot of the power of SimpleHMI programming. Every time your SPLat program defines an event and names an event handler, a new, totally unique event code is set up in the SimpleHMI. Thus every event is unique, and unambiguously tied to its own event handler. A program may contain up to 127 event handlers. You can reference an event handler multiple times.

Consider this: SimpleHMI has a limited number of buttons, which can be re-used on different screens. On screen A button 1 may be the "Pump On" button. On screen B it may be the "Launch Missile" button. For each use you will have a separate handler subroutine. Now, it is only if screen B is actually showing that pressing that button will send back the event code for Launch_Missile. This means your program does not have to keep track of which screen it is showing in order to know how to respond to button 1.

Here's a program that illustrates this point (You will get best value from this if you have indicator lights on outputs 0, 1, and 2 of your controller). There is a detailed description of each segment after the program. In this example I am using specific buttons to illustrate a point. Since the original writing the need to explicitly nominate buttons is all but eliminated but the point remains to be made:

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

Before running this example make sure the SimpleHMI in SPLat/PC is set to Width = 40 (Use the Width button on the SimpleHMI tab)

;Demonstrate re-use of buttons, and unique event handlers for each use
;**** This example is only suitable for use with the Windows SimpleHMI, e.g. in SPLat/PC *******
HMIPort	EQU	251	;Define the port number we want to use.
#       Open_Serial Port(HMIPort) User(38400,8,N)     ;Initialise the serial port
#       HMI Port(HMIPort) ConnectEvent(HMI_Connect)   ;Where to GoSub when the SimpleHMI connects
        LaunchTask      HeartBeat           ;Show we are alive
        RunTasksForever

;----- HeartBeat task, shows we are alive by flashing an output -----

HeartBeat:

        On              0

        Pause           2

        Off             0

        Pause           50

        GoTo            HeartBeat

;---- Event handler for the HMI Connected event generated by SimpleHMI -------

HMI_Connect:

        GoSub   HomeScreen     ;Paint home screen

        Return

;----- Event handler for Home button ------

HomeScreen:

#       HMI     Cls() HideAllButtons()

#       HMI     ButtonEvent(1,4,9,3,20,"Pump On",PumpOnEvent)

#       HMI     ButtonEvent(2, 9, 9, 3, 20,"Pump Off",PumpOffEvent)

#       HMI     ButtonEvent(3,14,9,3,20,"Launch Screen",LaunchScreen)

        Return

;----- Event handler for Pump On button ------

PumpOnEvent:

        On      1

        Return

;----- Event handler for Pump Off button ------

PumpOffEvent:

        Off      1

        Return

;----- Event handler to paint Launch Screen ------

LaunchScreen:

#       HMI SetBGColor(255,0,0) Cls() ButtonEvent(1, 10,9,10,20,"Launch Missile",Launch_Missile)

#       HMI     ButtonEvent(2,22,0,3,8,"Home",HomeScreen)

        Return

******  Launch the missile *********

Launch_Missile:

        LaunchTask      MissileLaunch  ;A new MultiTrack task

        GoSub           HomeScreen     ;Display the home screen

        Return

;==================================================================

;A separate MultiTrack task to create a "spectacular" launch display

bCount  defBYTE

MissileLaunch:

        SetMem          bCount,150       ;150 flashes

FLoop:

        On              2

        Pause           1

        Off             2

        Pause           10

        DMGNZ           bCount,Floop

        Killtask

Here's a blow by blow description of each segment of the program:

Startup code

Initialises the serial port, defines the handler for the HMI ConnectEvent, launches the heartbeat task and gets MultiTrack running.

Heartbeat task

Trivial. I use this because it's helpful to know during development that my program is actually running, especially as there's a 10 second delay when initialising a serial protocol.

Connect event handler

Just calls the subroutine that paints the home screen. This is also called from elsewhere.

Handler for Home button

Cls() clears the screen to the current background colour (more on that later). HideAllButtons() ... I'll let you work that out!

Once the screen is returned to a blank state, we position and label buttons 1, 2, and 3, and define their event handler names.

Pump handlers

Trivial, but notice how little effort it has taken to tie the buttons to the physical actions.

Launch screen

This is the program's second screen. It sets the background colour to red before clearing the screen. Then it defines a large, impressive "Launch Missile" button and a more modest Home button. Notice we are using button 1, which on the home screen is the Pump On button.

Launch button handler

This handler launches a fresh MultiTrack task whose job it is to manage the missile launch sequence.

MissileLaunch task

Creates a light and sound show (minus sound!) consisting of 150 rapid flashes of an output, then kills itself.

But there's a problem, Housten!

When you test the program you will discover that on returning to the home screen from the launch screen, the red background colour persists. That was not the intention.

Exercise: Study the program and fix it.

A subtle hazard

I have positioned the Launch Screen button on the home screen, and the Launch Missile button on the launch screen, deliberately overlapping each other. If you click very rapidly on the overlapping buttons, you will be able to crash the program. The reason is that every time you launch the missile you also launch another copy of the MissileLaunch task. When you get to the limit of 32 running tasks, the controller crashes.

Exercise: Find a solution.

Hint: Use a semaphore that is set when the launch missile task is launched, and reset when it completes. Use that semaphore to prevent it from being launched a second time when it is already running.

This effect does not occur in Android, because Android and Windows have different way of rendering overlapping buttons. This example is intended only for the Windows versions of SimpleHMI.

Note: It took me longer to write this page than to write the sample program.

Exercise: Modify the program so it is impossible to launch while the pump is on.


SimpleHMI: Displaying text

You have already seen text being displayed, and there's not much more to say about that.

Text is always displayed at the current cursor position, using the current foreground and background colours. There is only one size of text. If text runs off the edge of the screen it becomes invisible; SimpleHMI does not wrap text. Nor does it understand carriage return or line feed characters.


SimpleHMI: Cursor control

There are several ways of specifying screen coordinates for positioning text, buttons and other items. The available possibilities are:

  • Multiples of character width and height (which is all we have been doing so far)
  • Relative to any of the 4 edges or either centre line
  • As fractions of the screen height or width

The basic default screen has 40 columns of characters, numbered 0 to 39. The number of rows will depend on the screen dimensions of the target device. For example, Android devices come in a large number of screen resolutions such as 320x480, 1024x600 etc. It is possible to program a different number of characters across, but you have no direct control over the number of rows.

The screen can only be used in portrait orientation (relevant to Android).

The Cursor(Row, Col) hash function sets the cursor to an absolute location on the screen. If Row or Col is left blank, it is not changed (the comma must always be there).

Advance topic, handled elsewhere: It is also possible to position the cursor relative to the edges of the screen and relative to the centre of the screen, using character counts or fractions of the screen size.

The CursorRel(dRow, dCol) hash function moves the cursor relative to its current location on the screen.


SimpleHMI: Colours

Two colours can be set in SimpleHMI: The foreground colour, which applies to text, and the background colour. Colours are specified as a Red, Green, Blue triplet. Some common colours are listed here.

SPLat/PC build 244 or later contains a colour picker under the Window menu. It copies its result to the clipboard as an RGB triplet when you click its OK button.

To help you manage colours more easily, SimpleHMI has some unique functions PushColours(), PopColours(), PopFGColour(), and PopBGColour(). These can be useful when you have several different MultiTrack tasks writing to different parts of a screen, to prevent your colours getting mashed up.

The hash functions will respond to the US spelling "color" as well as the correct spelling, "colour" :-)


SimpleHMI: User input

SimpleHMI provides a high level hash function, IntInputEvent(), to allow a user to input unsigned integer data. You can specify optional minimum and maximum values, and SimpleHMI takes care of ensuring that the Enduser either supplies a value between the limits or "cancels out" of the operation. The maximum can be up to 9-9's (999,999,999).

When a enduser enters some data in response to an IntInputEvent() hash function, an event is raised as per usual. When your event handler is called, X will contain True (non-zero) if the enduser actually supplied a number, False if they Cancelled out. The number will be in W.

Example:

This example lets the enduser change 3 parameters within set limits. It uses the hash function FloatVar() to display the current value of each parameter, and IntInputEvent() to solicit a new value from the enduser.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

************ SimpleHMI: user data entry ****************

kHMIEOMCode EQU 4   ;Termination character used in messages back from the SimpleHMI (*** Cannot be changed ***)
fParam1:  defFLOAT  ;3 user settable parameters
fParam2:  defFLOAT
fParam3:  defFLOAT

#       Open_Serial User(38400,8,N)

#       HMI_Disp ConnectEvent(HMIConnected)

;Initialise the user settable parameters

        fLoadW          5

        fStore          fParam1

        fLoadW          12

        fStore          fParam2

        fLoadW          300

        fStore          fParam3

        GoSub           HomePage

        LaunchTask      HeartBeat

        RunTasksForever

;=======================================================

HMIConnected:  ;Runs whenever HMI sends a Connected event

        GoSub           HomePage

        Return

;=======================================================

;Homepage

HomePage:

#       HMI Reset() HideAllButtons()    ;Force screen to a known state

#       HMI ButtonEvent(, 5, 2, 3, 36, "Configure parameters", ConfigureParameters)

        Return

;==========================================================

;Parameter configuration screen

ConfigureParameters:

# HMI Reset() HideAllButtons()    ;Force screen to a known state

# HMI Cursor(2,3) SetFGColour(255,255,255) SetBGColour(0,0,0) Print("Setting 1: ") SetBGColour(0,255,255) SetFGColour(255,0,0)

# HMI FloatVar(fParam1,8,0) Print(" ") ButtonEvent(,1,28,3,10, "Change", ReqChangeParam1)

# HMI Cursor(7,3) SetFGColour(255,255,255) SetBGColour(0,0,0) Print("Setting 2: ") SetBGColour(0,255,255) SetFGColour(255,0,0)

# HMI FloatVar(fParam2,8,0) Print(" ") ButtonEvent(,6,28,3,10, "Change", ReqChangeParam2)

# HMI Cursor(12,3)  SetFGColour(255,255,255) SetBGColour(0,0,0) Print("Setting 3: ") SetBGColour(0,255,255) SetFGColour(255,0,0)

# HMI FloatVar(fParam3,8,0) Print(" ") ButtonEvent(, 11, 28, 3, 10, "Change", ReqChangeParam3)

        GoSub           UpMenuButton   ;Show a back button

        Return

;-------------------------- Up one menu level  USES BUTTON 7 --------------------------

UpMenuButton:

#       HMI ButtonEvent(, 22, 0, 3, 9, "<Back", ssUpMenuButton)

        Return

ssUpMenuButton:

        GoSub           HomePage

        Return

;=======================================================

;Event handlers for when user clicks one of the "Change setting" buttons

ReqChangeParam1:

#       HMI      HideAllButtons() IntInputEvent("New value for setting 1", 0, 100, GotChangeParam1)

; Get user to supply an integer value ___|              |                  |   |            |

; Prompt text___________________________________________|                  |   |            |

;Minimum value. Not enforced if blank______________________________________|   |            |

;Maximum value. Not enforced if blank__________________________________________|            |

;Do this GoSub when user enters acceptable data ____________________________________________|

        Return

ReqChangeParam2:

#       HMI HideAllButtons() IntInputEvent("What do you want setting 2 to be?", 10, 20, GotChangeParam2)

        Return

ReqChangeParam3:

#       HMI HideAllButtons() IntInputEvent("How many jelly beans in the jar?", 100, 1000, GotChangeParam3)

        Return

;=======================================================

;Event handlers for when enduser enters new data

GotChangeParam1:

       GoIfZ            GCP_Exit        ;g/ user Cancelled

       fStore           fParam1

       GoTo             GCP_Exit

GotChangeParam2:

       GoIfZ            GCP_Exit        ;g/ user Cancelled

       fStore           fParam2

       GoTo             GCP_Exit

GotChangeParam3:

       GoIfZ            GCP_Exit        ;g/ user Cancelled

       fStore           fParam3

;       GoTo             GCP_Exit

GCP_Exit:

       Gosub            ConfigureParameters  ;Back to param change screen

       Return

;================ Onscreen heartbeat (just for fun) =======================

HeartBeat:

#       HMI     PushColours()  SetBGColour(255,0,0) Cursor(24,38) Print("   ")   PopColours()

        Pause   10

#       HMI     PushColours()  SetBGColour(64,0, 0) Cursor(24,38) Print("   ")   PopColours()

        Pause   90

        GoTo    HeartBeat


SimpleHMI: Speech output

SimpleHMI provides text to speech for the Android platform, though not for Windows. This is documented under the hash functions SpeakText and OldSpeakNumb, including an example.


SimpleHMI example: PIN numbers

This example shows you a way of defining and changing PIN numbers on SimpleHMI. This can provide better security, and be easier for an Enduser, than the PIN provided by the Bluetooth pairing processes. It also illustrates the use of the Trace feature to trace what is happening in the program if it is run with the SPLat/PC version of SimpleHMI. The program initially offers you a single button choice: Ask for PIN. When that is clicked, the program asks you to input the pin (which defaults to 1234). If you get the PIN right it gives you an option to change the PIN.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;SimpleHMI PIN example.
;This is the main program. A secondary program forces the PIN back to a known setting
fPIN    mEQU    0,4                     ;PIN is stored as a float in Shadow memory at 0

#       Open_Serial User(38400,8,N)

        GoSub           InitializePIN   ;Make sure at least a default PIN is set

        GoSub           HMI_Connect    ;Paint the initial screen

#       HMI ConnectEvent(HMI_Connect)  ;Declare the handler for the Connected event

        LaunchTask      HeartBeat      ;Blink an output to show we are alive

        LaunchTask      Crash          ;Catch an input, crash to get back control of serial port

        RunTasksForever

;Connected event: Paint the screen

HMI_Connect:

# HMI_Trace Print("Connect") NL()

# HMI Reset() Cls() HideAllButtons()

# HMI Cursor(1, C-9) Print("PIN number example")

# HMI ButtonEvent(, C-3, 0.3, 6, 0.4, "Ask for PIN", evGetPIN)

        return

evGetPIN:

# HMI_Trace Print("evGetPin") NL()

# HMI Reset() Cls() HideAllButtons()

# HMI Cursor(1, C-9) Print("PIN number example")

# HMI IntInputEvent("Enter the PIN", 1000, 99999, evTestPIN)

        Return

fTemp:   defFLOAT

;Here when enduser enters a PIN (X = True, PIN in W) or Cancels (X = False)

evTestPIN:

# HMI_Trace Print("evTestPIN") NL()

       GoIfF            NoPIN                      ;g/ user Cancelled, nothing more to do

       fStore           fTemp                      ;Save enduser's input

       ShadowRead       fPIN,4                     ;Restore the old PIN

# HMI_Trace Print("Entered", f(fTemp,6,0), " PIN is", f(fPIN, 6, 0))  NL()

       fRecallQ         fPIN

       fTestWeqQ

       GoIfT            GotPin

       LaunchTask       PINFailed

       Return

PINFailed:

# HMI_Trace Print("PINFailed") NL()

# HMI Cursor(5, C-8) SetFGColour(255,0,0) Print(" **** FAIL **** ") SetFGColour(255, 255, 255)

        Pause           100

        GoSub           HMI_Connect   ;Repaint the home screen

        KillTask

NoPIN:

        GoSub           HMI_Connect   ;Repaint the home screen

        Return

GotPin:

# HMI_Trace Print("GotPin") NL()

# HMI Reset() Cls() HideAllButtons()

# HMI Cursor(1, C-9) Print("PIN number example")

# HMI ButtonEvent(, C-5, 0.3, 6, 0.4, "Enter new PIN", evAskNewPin)

# HMI ButtonEvent(, C+5, 0.3, 6, 0.4, "Go back", HMI_Connect)

        return

evAskNewPin:

# HMI_Trace Print("evAskNewPin") NL()

# HMI IntInputEvent("Enter the PIN", 1000, 99999, evGotNewPIN)

       Return

evGotNewPIN:

;Here when enduser enters a new PIN (X = True, PIN in W) or Cancels (X = False)

# HMI_Trace Print("evGotNewPIN") NL()

       GoIfZ            NoPIN      ;g/ user Cancelled, nothing more to do

       fStore           fPIN

       ShadowWrite      fPIN,4

# HMI_Trace Print("New PIN:") floatvar(fPIN,6,0)  NL()

        Return

********************* Minor stuff ***************************************************

;Blink an output to signal the program is running

HeartBeat:

        On              0

        Pause           1

        Off             0

        Pause           25

        GoTo            HeartBeat

;Deliberately crash the program to get back control of the serial port, so SPLat/PC can connect.

Crash:  WaitOnK         0

Here    GoSub           Here

;Force PIN to be > 1000. Anything less is assumed to be uninitialized.

InitializePIN:

        ShadowRead      fPIN,4

        fRecallW        fPin

        FLoadQ          1000

        fTestWgeQ

        RetIfT

        fLoadW          1234

        fStore          fPin

        ShadowWrite     fPIN,4

        Return


SimpleHMI: Programming beyond the hash

The hash commands and function provided in support of SimpleHMI will cover most of what you would or could do with SimpleHMI. However, there may be times when you need a little bit more.

The most likely case is that you want to display text in a form not catered for by hash functions. This page will tell you what to do.

When text is sent to SimpleHMI for display, it is wrapped in a "datagram" or packet with the following content:

'01,"@","Your text",'03

The '01 (one character containing hex 01) signals the start of a command to SimpleHMI. The "@" is the command to SimpleHMI to display text. The '03 at the end signals the end of the text to display, and the end of this command to SimpleHMI.

Example:

Position the cursor at (5,10), print a label and the contents of X as a signed decimal number:

#       HMI Cursor(5,10) Print("X =")
        iiPrintText     251,'01,"@"     ;"Header" of a text display command to SimpleHMI
;vvvvvvvvvv
        iisPrintXFW     251             ;251 is the serial port
;^^^^^^^^^^
        iiPrintText     251,'03         ;SimpleHMI command terminator

Between the ;vvvvv and ;^^^^^^ markers you can have anything you like that results in characters coming out of the serial port, so long as no character code is less than '20 (which might scramble the display).


SimpleHMI: Getting fancy with cursor positioning and screen sizes

One of the challenges of programming for Android devices is that they come in a huge variety of screen resolutions (pixel counts) and aspect ratios. The aspect ratio is the ratio between the long side and the short side. Typical aspect ratio figures are 4:3 (old TV), 3:2, 5:3 and 16:9 (modern wide-screen TV). Graphic display resolutions on Wikipedia. If you are programming for a single "target" device, it is fairly simple. If you are writing a program that should look OK on many different devices, it gets a bit trickier.

To help you out with all this we have done three things:

  1. Provided a way of specifying screen positions relative to the screen size, screen edges and screen center lines, and the sizes of buttons relative to the screen size. This will allow you, without too much difficulty, to lay out a screen that can flex and look reasonably balanced with different device sizes.
  2. Provided a way for you to specify the number of characters to fit into the width of the screen. You can for instance fit more onto a 10" tablet than onto a 3.5" 'phone.
  3. Made the SimpleHMI in SPLat/PC configurable for different screen aspect ratios. This will allow you to test how your SPLat program will work on different Android devices.

In practice is is virtually impossible to accurately simulate every possible Android device without access to a physical sample. Even then, it would not be sure-fire, because there is no guarantee that different versions of the Android operating system won't use up different amounts of screen space for status bar, default soft buttons etc. What the SimpleHMI in SPLat/PC does is allow you to set the number of character columns across, and the aspect ratio. It will then estimate the number of character rows.

The ScreenWidthEvent hash function lets you set the number of characters across the SimpleHMI screen. In the SimpleHMI device a special routine identifies the largest font size that will fit the required number of characters. An event is raised when this is done, so your program can draw or re-draw the screen. Note that the SimpleHMI device is not able to re-draw anything automatically when you change the screen width.

In This Section

SimpleHMI: Screen formats in SPLat/PC

SimpleHMI: Screen relative cursor positioning

SimpleHMI: Specifying the size of buttons and other items


SimpleHMI: Screen formats in SPLat/PC

In SPLat/PC Build 172 or later we have added controls to let you set the SimpleHMI aspect ratio and screen width (characters across).

The screen shot above shows the following controls down the righthand side:

Connect button

Clicking this sends a Connected event code to the SPLat controller.

Reset button

Clicking this resets the SimpleHMI display to the default screen (no text, default buttons) using the current aspect ratio and width settings.

Ruler

Draws ruler lines (as shown above with a cyan background colour)

A/R panel

The buttons in this panel let you set the screen aspect ratio

Width

Lets you set the number of characters across the screen (hint, after setting the width, click Ruler to find out how many rows the program estimates will fit on an Android device)

Show log

Exposes the logging window and re-draws the SimpleHMI screen to fit


SimpleHMI: Screen relative cursor positioning

The cursor in SimpleHMI is an imaginary point on the screen that is used as the anchor for text. The datum point is the top lefthand corner of the screen. The cursor can be positioned in several ways:

  • To an absolute text row and column number.
  • A given number of character sizes from the righthand edge and/or bottom edge.
  • At a decimal fraction of the screen width or height, for example 0.25 for one quarter of the screen extent.
  • A certain number of character widths or heights from either centre line of the screen.

In addition to being simple numbers, the row/column coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a row specifier | As a column specifier
5 | Row number 5 from the top, counting from 0. This can be a fractional number such as 5.5 | Column number 5 from the left, counting from 0
-5 | Row 5 from the bottom, counting from 0. This can be a fractional number such as -5.25 | Column 5 from the right, counting from 0
0 | The top character row | The left-most character column
-0 | The bottom character row | The right-most character column
0.25 | One quarter of (0.25 times) the screen height from the top. Fractional number between -1 and +1 are interpreted as a fraction of the screen. | One quarter of the screen width from the left.
-0.25 | One quarter of the screen height from the bottom. | One quarter of the screen width from the right.
C | The top-bottom centre of the screen. | The left-right centre of the screen.
C+5 | Five character rows down from the centre | Five character widths right from the centre.
C-5 | Five character rows up from the centre | Five character width left from the centre.
C+.25 | One quarter character row down from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | One quarter character width right from the centre.
C-3.25 | Three and a quarter character rows up from the centre. After a "C" all numbers are interpreted as multiples of the character pitch. | Three and a quarter character widths left from the centre.


SimpleHMI: Specifying the size of buttons and other items

This applies to (at time of writing) buttons and horizontal bar graphs.

The size of buttons can be specified as:

  • A multiple of the default character width and height, need not be an integer.
  • A decimal fraction of the screen width or height, for example 0.2 for one 5th.

In addition to being simple numbers, the width and height coordinates may also be specified in relative or proportional terms. The following table illustrates the possibilities.

Format (example) | As a height specifier | As a width specifier
5 | 5 character rows in height | 5 character rows in width
0.25 | One quarter of (0.25 times) the screen height. Always calculated as a full number of rows, always rounded down, but forced to be at least 1. | One quarter of (0.25 times) the screen width. Always calculated as a full number of columns, always rounded down, but forced to be at least 1.


SimpleHMI: The standalone Windows version

The standalone (windows) version of SimpleHMI lets you provide a PC-based operator interface for your products with very minimal programming. This is not the ultimate user interface - a custom written Windows app can provide a lot more sophistication and eye candy - but it is very quick and easy to implement.

Standalone SimpleHMI has been made specifically with the Enduser in mind. It has the absolute minimum of settings (essentially, just COM port selection and a Connect button). However, your program can command SimpleHMI to show other controls if required.

There could be times when you want to use the terminal function in the standalone Windows version of SimpleHMI. Simply include the switch /T in the command line/shortcut and the Terminal tab will be visible. The shortcut target should look something like this:

"C:\Program Files (x86)\SPLat\SimpleHMI_Standalone\SimpleHMI.exe" /T


SimpleHMI: The logging feature

SimpleHMI lets you do logging of data from the host device (SPLat or other). The logging facility features:

  • Up to 20 separate log files can be written with different data.
  • File names can be automatically date stamped and sequence numbered, or both
  • You can control the file name extension, for example .txt, .csv or .log
  • In Android, the log file directory is pre-determined. Your Enduser can find it easily when connecting via USB
  • Individual records can be date and/or time stamped

Logging uses the HMI_Log hash command. Please click that link and read up.

Note that in SPLat/PC the SIO/SimpleHMI window must be active for logging to take place. A file path (folder name) must be specified for the log file, in the Settings tab of the SIO window. The file name itself is embedded in the SPLat program.

Example 1:

This is a simple example. It uses an MS120, but you could easily convert it to an MMi202 by re-defining the input buttons. The program simply logs button presses.

When one of the buttons, named iStart is pressed, the program opens the log file and records the date. When subsequently another button, iLogOne is pressed, it logs the time. Finally, a 3rd button, iAllDone, halts the program by brutally crashing it. This allows SPLat/PC to connect to the controller.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;Input definitions to suit MS120
iStart          iEQU    12
iLogOne         iEQU    13
iAllDone        iEQU    16

        ON              20      ;LCD backlight on MS120

        OBCA_Mode       2

;                        0123456789123456

        OBLCD_Text      "Starting serial "

        OBLCD_Text      "(10S pause) "

#       Open_Serial User(38400,8,N)         ;Initialise the serial port

        LaunchTask      Capture

        LaunchTask      HeartBeat      ;Blink an output to show we are alive

        RunTasksForever

;----- Capture task -----

Capture:

        OBLCD_Cls

;                        0123456789123456

        OBLCD_Text      "Push Start"

        WaitOnK         iStart           ;Wait for user start command

        OBLCD_Cls

        OBLCD_Text      "Push LogOne or"

        OBLCD_SetCur    1,0

        OBLCD_Text      "AllDone button"

#       HMI_Log         FileOpen(mylog.txt,0)

#       HMI_Log         Print("Log opened ") YYMMDD() LogWrite()

Loop1:

        YieldTask

        GoIfInK         iLogOne,LogOne   ;Test LogOne button

        GoIfInK         iAllDone,AllDone ; Quit?

        GoTo            Loop1

LogOne:

#       HMI_Log         Print("LogOne pressed at ") HHMMSS() LogWrite()

        GoTo Loop1

AllDone:

        OBLCD_Cls

        OBLCD_Text      "All done"

#       HMI_Log         Print("Shut down ") YYMMDD() Print("at ") HHMMSS() LogWrite()

;Deliberately crash so SPLat/PC can get back control and connect

Here:   GoSub           Here

;Blink an output to signal the program is running

HeartBeat:

        On              8

        Pause           1

        Off             8

        Pause           25

        GoTo            HeartBeat

Example 2:

This is a more elaborate example, maintaining several logs in different modes.

The following program maintains 3 separate logs:

  1. A log of inputs 0 to 3, sampled once every 10 seconds. The file name is date stamped. Each record is time stamped.
  2. A log of each change of state on input 4. The file name is numbered. Turning input 3 on increments the file number. Each record is time stamped.
  3. A log of user presses on a button on the SimpleHMI screen. Uses a simple file name (no date or numbering). Each record is date and time stamped.

***************** SimpleHMI logging example *****************************

#       Open_Serial User(38400,8,N)

        GoSub           HMI_Connect    ;Paint the initial screen

#       HMI ConnectEvent(HMI_Connect)  ;Declare the handler for the Connected event

        LaunchTask      Watch4Inputs   ;Log inputs 0-3 every 10s

        LaunchTask      WatchInput4    ;Log all changes on input 4

        LaunchTask      WatchInput3    ;Task to create new files for Input 4 log

        LaunchTask      HeartBeat      ;Blink an output to show we are alive

        LaunchTask      Crash          ;Catch input, crash to get back control of serial port

        RunTasksForever

;Connected event: Paint the screen

HMI_Connect:

# HMI Reset() Cls() HideAllButtons()

# HMI_Log FileOpen(Watch4Inputs.csv, 1)   ;Mode 1 = Dated file

# HMI_Log FileOpen(WatchInput4.log, 2)    ;Create/initial open. Mode 2 = numbered file

# HMI_Log FileOpen(UserInputs.txt, 0)

# HMI Cursor(1, C-7) Print("Logging example")

# HMI ButtonEvent(, C-3, 0.3, 6, 0.4, "Enduser event", evEnduser)

        return

;==================================================================================================================

;A log of inputs 0 to 3, sampled once every 10 seconds. The file name is date stamped. Each record is time stamped.

bInput0to3:     defBYTE

Watch4Inputs:   ;Log inputs 03 every 10s

        InputFM         0                     ;Sample inputs 0-7

        LoadX           'F                    ;Select lower 4 bits

        AndM                                  ;Mask off top 4 bits

        Store           bInput0to3            ;Save so HexVar can get hold of it

# HMI_Log FileOpen(Watch4Inputs.csv, 1)       ;Mode 1 = Dated file

# HMI_Log  HHMMSS() Print(",")  HexVar(bInput0to3) LogWrite()

        Pause           1000    ;10 seconds

        GoTo            Watch4Inputs

;==================================================================================================================

;A log of each change of state on input 4. The file name is numbered.

;Turning input 3 on increments the file number. Each record is time stamped.

;This uses two tasks. One watches for and logs changes. The other takes care of the new-file behaviour

WatchInput4:    ;Log all changes on input 4

# HMI_Log FileOpen(WatchInput4.log, 2)

# HMI_Log Print("Monitor started at ") hhmmss() LogWrite()

        Input           4

        GoIfT           WI4onA

WI4off:

        WaitOn          4

WI4onA:

# HMI_Log FileOpen(WatchInput4.log, 2)     ;Switch to the file

# HMI_Log Print("Input 4 on  at ") hhmmss() LogWrite()

WI4on:

        WaitOff         4

# HMI_Log FileOpen(WatchInput4.log, 2)     ;Switch to the file

# HMI_Log Print("Input 4 off at ") hhmmss() LogWrite()

        GoTo            WI4off

;------------------------------------------

WatchInput3:    ;New file for Input 4 log

        WaitOnK          3

# HMI_Log NewFile(WatchInput4.log)  ;Signal new file creation on the next record write

        GoTo           WatchInput3

;==================================================================================================================

;A log of user presses on a button on the SimpleHMI screen. Uses a simple file name (no date or numbering).

;Each record is date and time stamped.

;Apart from the initial FileOpen in the ConnectEvent handler, this is the the whole thing.

evEnduser:

# HMI_Log FileOpen(UserInputs.txt, 0) Print("Pressed at ") hhmmss() Print(" on date:") yymmdd()  LogWrite()

        Return

********************* Minor stuff ***************************************************

;Blink an output to signal the program is running

HeartBeat:

        On              0

        Pause           1

        Off             0

        Pause           25

        GoTo            HeartBeat

;Deliberately crash the program to get back control of the serial port, so SPLat/PC can connect.

Crash:  WaitOnK         0

Here    GoSub           Here


SimpleHMI: The trace feature

In SPLat/PC the SimpleHMI provides a second screen for event tracing. This has its own hash command, HMI_Trace, with its own set of permitted hash functions. The trace window displays whatever is sent by the SPLat controller on a scrolling display, so it can provide a record of past events. The same information can also be saved to a disk file.

Run the following program in a SPLat controller to see how tracing differs from the regular SimpleHMI screen. The trace screen is made visible by the Show Trace button in the lower righthand corner of the SimpleHMI screen. You can write the trace data to disk as it comes in; select the Settings tab to set the trace file name.

Trace is designed specifically to assist in debugging. There is also a data logging feature that exists in the Android version as well.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;=============== Demonstrating the trace screen in SimpleHMI =======================
;Demonstrating the trace screen in SimpleHMI in SPLat/PC
fTimer  defFLOAT
hTimer  defByte
# HMI_Trace TraceOn()             ;Enable trace output
#	Open_Serial User(38400,8,N)    ;Initialise the serial port
#  HMI ConnectEvent(SimpleHMI_Init)
        GoSub           SimpleHMI_Init
        LaunchTask      TimeTickFloat
        LaunchTask      TimeTickHex
        RunTasksForever

;Generate and display a floating point time counter
TimeTickFloat:
        Pause           20
        fRecallW        fTimer
        fLoadQ          0.2
        fAdd
        fStore          fTimer
# HMI Cursor(0,5) FloatVar(fTimer,6,1)
        GoTo            TimeTickFloat

;Generate and display an 8-bit timer counter

TimeTickHex:

        Pause           100

        IncM            hTimer

# HMI Cursor(0,15) HexVar(hTimer)

        GoTo            TimeTickHex

;Button handlers

TheFloatButton:

#       HMI_Trace Print("Float Button ") FloatVar(fTimer,6,1) NL()

        Return

TheHexButton:

#       HMI_Trace Print("Hex Button ") HexVar(hTimer) NL()

        Return

;----- (re-)initialise the SimpleHMI screen ---------------

SimpleHMI_Init:

# HMI Reset() HideAllButtons() SetBGColour(200,200,200) SetFGColour(0,0,128) Cls()

# HMI ButtonEvent(,10,9,3,20,"Click me! (Float)",TheFloatButton)

# HMI ButtonEvent(,15,9,3,20,"Click me! (Hex)",TheHexButton)

        Return


SimpleHMI: Hosts other than SPLat

This tutorial has covered programming of a SPLat controller to utilise SimpleHMI. However, SimpleHMI can be used with other hosts such as Arduino, Stamps and other hobby products.

An ideal host will be something that can do serial communications concurrently with other operations. This implies some level of multitasking or interrupt driven system and hardware serial I/O (UART). The host must be prepared to receive incoming serial data from button presses any time a button is being displayed.

But even if you are using something incredibly basic like a Picaxe, you can get some use out of SimpleHMI. We deliberately made the default button codes "1" though "8" plus a '04 terminator. A Picaxe could simply look for the numeral and discard or ignore the terminator. It would still have to "camp" on the serial input pin, doing nothing else, when expecting a button press. This cannot produce a robust industrial grade solution, but for a hobby project could be quite adequate. Picaxe does not have enough code space for really elaborate SimpleHMI displays.

Slightly up the ladder are Arduinos, some of which have true UARTS available. With this it should be possible to set up an interrupt service routine to decode incoming characters and generate callbacks, roughly equivalent to the event handlers synthesised by SPLat hash commands.

We have not actually tried programming any 3rd party products for SimpleHMI, and would welcome input from anyone who does it.

SimpleHMI low level serial communications


Tutorial: SPLat Call Home (SX10509) programming

In this tutorial I will introduce you to the basics of programming a SPLat controller with attached SX10509 SPLat Call Home (SCH) board to interact with a distant web server. I will show the development of a SPLat program that demonstrates how data can be sent to a server and how to get data back. I will also be introducing you to a special "sandbox" page we have put up on our server, which will let you play around with the program with a real webserver. This sandbox gives you the ability to inspect what was sent from the SPLat and to control what gets sent back, and is a great debugging tool.

Outcomes (what you will get out of this tutorial)

  • An understanding of how a SPLat program can effectively use a SX10509 SCH board to interact with an Internet server.
  • An understanding of the data formats frequently used to exchange data with a server using GET and POST methods.
  • A very useful tool (the sandbox) for testing and debugging SCH programs.
  • A working example program that you can build upon for your own project.

Prerequisites (what you need before you start)

  • A working knowledge of MultiTrack, SPLat semaphores, and using Xwire
  • Good skills with SPLat/PC
  • A SPLat controller with an LCD (MMi202 or MS120, or any custom SPLat with LCD)
  • An SX10509 wired to the SPLat controller via Xwire

What this tutorial does not cover

I do not cover programming of the server. That is a whole different area. Typically, the server would be programmed in php, by someone with php skills (fairly common in web programmers). It could also be programmed in C++ or any one of a number of other languages. What this tutorial will do, is provide you with the tools to delineate between the SPLat side of a project and the server side. You will be able to know, and to show, that the SPLat side is working and to show exactly what you are sending, and to prove that you are processing known received data properly.

SPLat controls can provide the server end software for you, and even host it, if required.

Previous Topic

Next Topic

In This Chapter

SPLat Call Home: The sandbox

Example: SPLat Call Home sandbox program


SPLat Call Home: The sandbox

Point your browser to http://www.splatco.com/sch/sandbox.php (copy and paste the URL (address) into your browser). This is the "sandbox" page we have created as a tool to assist in developing SPLat programs for SPLat Call Home (SCH). It is hosted on the same server as our website, at a 3rd party web hosting company in the USA.

The sandbox consists of a php script, linked to a web page. If you connect to it from a web browser, it will return and display a web page which shows what you sent it. If you send to it from a SX10509 SCH board, it will return some data to the SCH, and simultaneously update the web page to show you what the SCH sent and what the sandbox script sent back to the SCH. You can also influence what is to be sent to the SCH, on the web page. Hence, the sandbox is a useful tool for monitoring what comes out of SCH and for sending back some known data.

The sandbox is a useful tool for confirming that the SPLat SCH programming is working correctly against a known, good, php script in the server. This means you won't have to try and debug both ends at once. This can be particularly valuable if someone else is doing the server programming. The overall strategy then is to get your code in the SPLat working against the sandbox, and then switch over to your own php on your own server. At that point it would be a good idea to implement bits of the sandbox on your server, so you can test it against a known good SPLat program.

Once you have displayed the sandbox page using the URL http://www.splatco.com/sch/sandbox.php, you can try adding a "query string" to the URL. Query data is something following a question mark after the filename sandbox.php.

For example, you could try this:

http://www.splatco.com/sch/sandbox.php?a=5&b=-9

The query string here is a=5&b=-9. This consists of two "name-value pairs", separated by an ampersand - &. The first name-value pair is a=5, the second is b=-9. There is no law that says you must send data in this format (except for the question mark), but it is the convention, and php is very good at extracting data in that format.

You may recognize this format from Google queries like http://www.google.com/search?q=splat+call+home&ie=UTF-8 that will appear in your browser's address bar. You will notice this contains plus-signs between the words, in place of spaces. It is illegal to send spaces (and several other characters) in a query string. For Google queries plus signs can substitute for spaces. More commonly "percent escaped" characters are used, for example %20. 20 is the hexadecimal code for an ASCII space character. A number of characters must be percent escaped, including space, carriage return, ampersand and the percent sign. More information..

Don't use your browser to explore what will work in a query string. Browsers are very good at "fixing" up incorrect syntax, so any conclusions you might draw about what works and what doesn't work could be incorrect.

The sandbox distinguishes between 3 types of query string:

Query string (after ?)

Sand box response back to SCH

Meaning / notes

(Nothing)

05:24:01 GMT

The current GMT time

a=5&b=-9

-4

the sum a + b

config

therm=67.78
cktime=99
pump=1
valve=0

4 name-value pairs separated by carriage return ('0D) characters

Have a play with the sandbox from your browser. It will always show you what it received, and if you send it name-value pairs it will show you the individual variable.

When you use the sandbox from a browser. it cannot show you what would have been sent back to a SCH board. Once you interact with it with a real SCH board, it will show you what it sent back to the board.

In This Section

SPLat Call Home sandbox: Configuring the SX10509


SPLat Call Home sandbox: Configuring the SX10509

The SX10509 can be pre-configured for the target server and script. To use it with the sandbox as the target, the settings can be found on the Sandbox page. How to configure the server settings is documented in the SX10509 documentation here.

Our sample SPLat program, which works with the sandbox, assumes the following Xwire settings in the SX10509:

Make sure your SX10509 has these setting before working through the sample program.


Example: SPLat Call Home sandbox program

The sample program presented here works with the sandbox. Please play with the sandbox from your browser to get a feel for it, and make sure your SX10509 is configured as described above, before proceeding.

The program is too large to present and describe all in one hit. I will therefore describe its development in small steps, and gradually "glue them together" to form the completed program.

Preview of what it does

You can download the whole program here: sandbox1.spt. If you wish, you can download it and run it now, to see what it does. You will need:

  • A MMi202 with LCD or an MS120, or any custom SPLat that has an LCD and Xwire;
  • An SX10509 SPLat Call Home board wired to the above controller via Xwire and configured as described earlier;
  • Power to the controller and SX10509;
  • An Internet connection via Ethernet. This must be on a local network that has no security settings that will prevent an ordinary computer from getting out to the internet without entering a user name/password. In other words, I should be able to walk in, plug my laptop into the network and get out to the Internet from my browser with no further ado;
  • SPLat/PC;
  • A computer connected to the Internet and logged onto the sandbox page.

Open the program in SPLat/PC. At the top is a section labelled I/O Assignments. This has two groups of definitions, one for MMi202 and one for MS120. "Uncomment" the lines that match your board, and Comment out, or delete, the other group. If you have a custom SPLat, you will need to create your own assignments. Use some regular digital inputs if your controller has no push buttons. You actually only need 3. Contact your regular support engineer if in doubt.

Identify the physical inputs (push buttons) corresponding to iBtnA, iBtnB, and iBtnC.

Translate the program, download it to the controller, and set it running (This will not work in simulation!). The program should identify itself on the screen.

Log on to the sandbox page if you are not already there.

Now the big test: Press button A. If it all works, the time in GMT should show on your LCD, and the sandbox page should show what was sent from the SX10509 and what was sent back.

If this fails, re-check your connections, make sure the SX10509 is plugged into a working Internet connection and that its activity lights are flashing.

If that fails, connect to the SX10509 from your browser and select the Status tab. When you push a button you should see activity in the data windows. If not, double check the Xwire connections and the Xwire configuration.

If you cannot connect to the SX10509, you most likely have a network problem. See your network administrator.

Once you have button A responding, try the other buttons. Button B will simply display -4. Button C will display data from the Simulated Application part of the sandbox. When you change that data you must click the Save button on the web page before it can be sent to the SX10509. The Pump and Valve settings are displayed on front panel LEDs.

Experiment with changing the data in the Simulated Application.

When this all works, take a break and start thinking about what you can now achieve with an Internet connected SPLat controller!

Dissecting the sample program.

In the following pages I will take you through the sample program a bit at a time. I will introduce it from the bottom up, so you can be sure to understand the programming techniques used, as well as the big picture.

Previous Topic

Next Topic

In This Section

Xwire data blocks and NVEM table

Setting the command byte, waiting for an echo

Subroutines for individual Network commands

Display contents of Xwire network Rx data block

The main loop

Test 1: Send empty GET, receive GMT

Test 2: Send two name-value pairs, display the response

Test 3: Retrieve multiple values from the server

Startup code

Exception handling


Xwire data blocks and NVEM table

Note: The program includes data block definitions and Xwire tables for the Date and Time addresses in the SX10509. No use is made of the date and time in this sandbox application, but the configuration code has been left in the actual program to provide you with an example for your own applications.

Xwire data blocks

The program uses Xwire to communicate with the SX10509. Xwire requires separate Tx (transmit) and Rx (receive) data blocks in RAM for each Xwire address. These are defined in the following lines, which set aside RAM:

;---- For Tx
kTxNetLength     EQU      32
                 defBLOCK 32         ;transmit data block
bTxCmd           defBYTE             ;command (bit 7 toggles the next iteration of the current command)
bTxLength        defBYTE             ;length of transmit data
kTxDataLen       EQU      30
abTxData         defBYTE  30         ;transmit data
;
;---- For Rx
kRxNetLength     EQU      32
                 defBLOCK 32         ;receive data block (chose 32 as this is the SCH default)
bRxCmdEcho       defBYTE             ;copy (echo) of the last completed command (will match bTxCmd when done)
bRxLength        defBYTE             ;receive data length
kRxDataLen       EQU      30
abRxData         defBYTE  30         ;received data

For the Tx, we set aside a block of 32 bytes of RAM. The first byte we name bTxCmd. This is where we will store commands to the SX10509. The SX10509 will respond each time that byte changes. The second byte we name bTxLength. For data transfers to the SX10509 this is set to the number of data bytes we are sending it. At the 3rd byte we set aside 30 bytes, naming the first one abTxData. This is where we can store up to 30 bytes of data to be sent to the SX10509.

For the Rx, the structure is very similar. When in use, the SX10509 will set the byte bRxCmdEcho to reflect the command just processed. If processing involves a round trip to a remote server, this could take some time.

NVEM table for Xwire communication to the SX10509

The NVEM table tells the SPLat how to communicate with the SX10509 via Xwire. This is a pretty standard Xwire master configuration table.

pXWireConfig:
   ;SCH Xwire NETWORK configuration
   NV0Byte     64                      ;Address of the SPLat Call Home board
   NV0Byte     bTxCmd                  ;Address of the Tx data block
   NV0Byte     kTxNetLength            ;Length of the Tx data block
   NV0Byte     bRxCmdEcho              ;Address of the Rx data block
   NV0Byte     kRxNetLength            ;Length of the Rx data block

   NV0Byte     255                     ;XWire config end of table marker


Setting the command byte, waiting for an echo

You need to understand both MultiTrack and SPLat semaphores to understand the following.

Down in the core of the program is a mechanism for setting the Tx command byte (bTxCmd in the Tx data block) and then waiting for an echo. This consists of a subroutine that is called with the required command byte in the X register, and a semaphore that is set only after the SX10509 has echoed the command. The steps for using this are:

  1. Load the required command byte into X
  2. Call the subroutine
  3. Wait for the "all done" semaphore to get set.

The SX10509 will only respond to a different command to the last one processed. For several commands of the same type in a row, the trick is to toggle (invert) bit 7 of the command byte. The subroutine below takes care of that under the hood.

Here's the first part of the code. This is the subroutine that gets called in step 2 above, with X set to the command byte.

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

SCH_SetCommand:
        Push
        Recall          bTxCmd
        Swap
        Store           bTxCmd
        Compare
        GoIfNZ          SCH_SC1         ; g/ new <> old
        NotS            7,bTxCmd        ; old = new ... toggle bit 7
SCH_SC1:
        ClrS            sSCH_GotEcho    ;Clear the Echo semaphore
        LaunchTask      tsk_SCH_AckWait ;Task that watches out for an echo.
        Return

sSCH_GotEcho:            defSEM          ;Signals completion of a SCH/Xwire command

The above code compares the new command with the previous one, which it retrieves from the Tx data block. It then toggles bit 7 if they are the same. Once that is done is clears the "all done" semaphore sSCH_GotEcho, and launches a task tsk_SCH_AckWait that is going to watch for a match between the sent command bTxCmd and the received echo byte bRxCmdEcho.

Here's the "wait task" tsk_SCH_AckWait:

tsk_SCH_AckWait:

        YieldTask

        Recall          bTxCmd          ;Get back the command just set

        Recall          bRxCmdEcho      ;read the Rx echo location

        Compare

        GoIfNZ          tsk_SCH_AckWait ; g/ different

        SetS            sSCH_GotEcho    ;Signal that the echo is received

        KillTask                        ;Job done!

This task just sits looping until the echo matches what was sent, then sets the semaphore and kills itself.


Subroutines for individual Network commands

There are a number of subroutines that use SCH_SetCommand to send specific commands to the SX10509. Most are quite trivial. Here's one of them:

;----- Send GET command to SCH
;      sSCH_GotEchois set once the command is echoed by SCH
SCH_Get:
        LoadX           kCMD_DoGet
        GoSub           SCH_SetCommand
        Return

There are several that follow the same pattern. They make it easier to code the higher level stuff, and make the program a lot more readable.

The subroutine for commanding a read from the SX10509 network Rx buffer is a bit more interesting. It is designed to use an offset, passed to it in the I (index) register, as a parameter in the command being sent to the SX10509. The Read command is documented here.

;----- Read from n/w Rx buffer in SCH to Xwire Rx data block
;      Starting offset in I
;      sSCH_GotEchois set once the command is echoed by SCH
SCH_Read:
        ItoX
        Store           bTxLength             ;Incremental offset
        LoadX           kCMD_Read
        GoSub           SCH_SetCommand
        Return


Display contents of Xwire network Rx data block

One of the functions performed in a couple of places in the sandbox program is to display the contents of the network Rx data block on the LCD, taking into account the number of bytes of available data, as indicated by bRxLength. This is done by the following subroutine:

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

;----- Display contents (up to 30 bytes) of Xwire network Rx data block
RxDCount:               defBYTE                 ;local loop counter
RxDisplay:
        OBLCD_Cls
        Recall          bRxLength               ;Get the byte count
        Push                                    ;Duplicate
        GoIfNZ          RxD1                    ;Test there's something there
        OBLCD_Text      "Nothing frm srvr"      ;Oops!
        Return
RxD1:
        Store           RxDCount                ;Save the loop counter
        LoadI           0                       ;Index into the Rx data block
RxDispLoop:
        iRecall         abRxData                ;Get the next byte
        OBLCD_CharX                             ;Display
        IncI                                    ;Increment the read index to the next character
        DMGNZ           RxDCount,RxDispLoop     ;Decrement and test the loop counter
        Return                                  ;All done

The main trick here is using the index register I to access a range of data bytes, while using a memory location (RxDCount) to count down the bytes being moved.


The main loop

If you understand how each of the above building blocks works, it will be very easy to understand the higher level parts of the program. The program contains just one MultiTrack task, called ServerUpdate, apart from the transitory task used by the low level SCH_SetCommand function.

The ServerUpdate task incorporates 3 separate "tests", which interact with the sandbox script on our server in 3 different ways. Each test is triggered by pushing a button. ServerUpdate contains a simple loop that sits looking for a button press. When one is detected it jumps to the corresponding code segment. Here's that loop code:

WAFbutton:
        Yieldtask
        GoIfInK         iBtnA,Test1
        GoIfInK         iBtnB,Test2
        GoIfInK         iBtnC,Test3
        GoTo            WAFbutton

Each code segment (test) does a GoTo back to WAFbutton when it is done.


Test 1: Send empty GET, receive GMT

The first test is the simplest. It uses a DoGet command to the SX10509 to make the SX10509 send a GET request to the server. The query string is empty (i.e. no query string). The server responds with an HTTP status code and the GMT time. The status code is seen by the SPLat program as a response to the DoGet command to the SX10509. The GMT time is retrieved from the SX10509's network Rx buffer using a Read command.

The code checks that the status code is 200. If it is, all is well, and it reads out from the SX10509, and displays, the data returned by the server. If the code is not 200, something went wrong, and the program gives up that test. The sandbox page lets you generate a dummy 299 code, so you can test your program's ability to detect and handle errors.

Send the GET

The first 6 lines of the Test1 code force the SX10509 to idle, clear its network Tx buffer (so the query string will be empty) and generate a GET request to the server. The server address used is the one from the server tab of the SX10509 configuration page. After each command is sent to the SX10509, the program waits for the sSCH_GotEcho semaphore to get set, at which time (and not before!) it is safe to send another command:

Test1:
        GoSub           SCH_Idle                ;Force the SCH into idle
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed
        GoSub           SCH_ClearTx             ;Make sure the SX10509 Tx buffer is clear
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed
        GoSub           SCH_Get                 ;Send a GET request to the server
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed

Retrieve and check the returned status code

When the server has responded to the GET request, the status code can be found in the network Rx data block as a floating point number. The next bit of code retrieves the code and tests if it is 200 (good) or anything else (bad). If it is good it jumps to program line Test1a. If the code is bad, the program displays a message on the LCD and loops back to WAFbutton to wait for another button press.

        fRecallW        abRxData                ;Get the status code -> W
        fLoadQ          200                     ;OK code
        fTestWeqQ                               ;OK?
        GoIfT           Test1a                  ;g/ good response

        OBLCD_Cls                               ;Display the bad status code
                        ;0123456789012345
        OBLCD_Text      "Error code "
        OBLCD_fDispW    3,0
        GoTo            WAFButton

Display the returned string

The data returned from the server is an ASCII string. It is currently sitting in the SX10509 network Rx buffer. To display it we first read it from the SX10509 to the Xwire Rx data block, then use a subroutine (one we prepared earlier) to display it.

Test1a:
        LoadI           0                       ;Offset into SCH network Rx buffer
        GoSub           SCH_Read                ;Read the SCH Rx buffer to the Xwire Rx data block
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed
        GoSub           RxDisplay               ;Display the result

        GoTo            WAFbutton

In this case we only need to read once from the SX10509 network Rx buffer, because we know it will have fewer characters for us than can fit in our Xwire Rx data block. The parameter I = 0 that we send to SCH_Read causes the read to be taken from the start of the SX10509 network Rx buffer.

Note: The GMT time in this example is formatted and supplied by the sandbox php code as an ASCII (human readable) text string. This is quite different to the built in Time and Date functions of the SX10509 board, which use an Internet time server and return the data to the SPLat board in a binary (non human-readable) format. The sandbox example does not use that feature of the SX10509


Test 2: Send two name-value pairs, display the response

Test 2 sends the query string "a=5&b=-9", and then displays the server response, which is the sum a+b, or -4. The code is written to make it easy for you to send other values of a and b.

Prepare

The first bit is the same as in test 1, where we Idle the SX10509 and then clear its network Tx buffer.

Test2:
        GoSub           SCH_Idle                ;Force the SCH into idle
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed
        GoSub           SCH_ClearTx             ;Make sure the SX10509 Tx buffer is clear
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed

We have to clear the buffer before writing to it, because writes to the SX10509 network Tx buffer always append to whatever is already there.

Write the query string

The next block of code writes "a=5&b=-9" to the SX10509 network Tx buffer. It does this in several small bits to make it easy for you to hack the code, and also to illustrate an important aspect of the iiPrintText and similar SPLat instructions.

        LoadI           0                       ;Write offset pointer for iiPrintText and similar instructions
        iiPrintText     abTxData,"a="           ;Write the first name
        LoadX           5                       ;Set the first value, use an integer
        iiuPrintXVW     abTxData                ;Write the first value
        iiPrintText     abTxData,"&b="          ;Write delimiter and the second name
        fLoadW          -9                      ;Set the 2nd value as a float
        iifPrintWVW     abTxData,10,3           ;Write the 2nd value, max 10 characters, max 3 decimal places

The instructions that start with ii all use the index register I as an offset from a base address, then increment I as they process several contiguous bytes of data. When the instruction has completed, I is left pointing one byte beyond the last byte processed. Hence, in the above code, the 4 separate elements of the total query string will be written to separate, contiguous area of the Xwire transmit data block that starts at address abTxData.

Write to the SX10509

Next we have to Write the query string to the SX10509 network Tx buffer. After the iiPrints (etc) above, I has been incremented by the total number of bytes written to the Tx data block. Because it started out containing 0, it now has the byte count.

        ItoX                                    ;Get the number of bytes written
        Store           bTxLength               ;Data length byte in the Xwire N/W Tx data block
        GoSub           SCH_Write               ;Set the command byte to Write
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed

Send a GET

Now the query string has been written to the SX10509 network Tx buffer, we can generate a GET request. This will be sent to the server with the just-written query string appended:

        GoSub           SCH_Get                 ;Send a GET request to the server
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed

Check the status code from the server

Just like in Test 1, the status code should be 200. This time I have chosen not to display it if it's bad - I simply abandon the operation.

        fRecallW        abRxData                ;Get the result code -> W
        fLoadQ          200                     ;OK code
        fTestWeqQ                               ;OK?
        GoIfZ           WAFbutton               ;g/ bad response - give up

Read and display the data returned by the server

This is just the same as before.

        LoadI           0                       ;Offset into SCH network Rx buffer
        GoSub           SCH_Read
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed
        GoSub           RxDisplay               ;Display the result

        GoTo            WAFbutton


Test 3: Retrieve multiple values from the server

This test shows how something entered on a web page can be sent to a SPLat and actioned. This is the basis of over-the-web remote machine control.

On the sandbox page there is an area at the bottom where you can enter two numerical values and two on/off settings. We will now fetch those from the server. The numeric values will be displayed on the LCD, while the two on/off settings will control front panel LEDs. (Remember, when you change any of these on the sandbox page you must click "Save" for the changes to become active).

The initial stuff

The codes starts of with the familiar actions, ending in sending the query string "config" (the query string doesn't have to be name-value pairs!). I'm not showing that code here. At the end of it the server's response will be in the SX10509 network Rx buffer. This consists of four name-value pairs separated by carriage return ('0D) characters.

Example:

therm=67.78
cktime=99
pump=0
valve=1

Initializing the parsing loop

The data that comes back from the server has to be parsed, meaning it must have its 4 component parts (the 4 variables) extracted. We will do this in a program loop, which works like this:

  1. Initialize the loop;
  2. Read one "chunk" from the SX10509;
  3. If there was nothing left in the SX10509, go to step 7;
  4. Determine which data item name is at the start of what we just read from the SX10509;
  5. Extract its value and act on it;
  6. Go to step 2;
  7. Finished.

Step 1, initializing the loop, consists simply of setting a variable, bSCH_RxBufferOffset, to 0. For each iteration of the loop bSCH_RxBufferOffset will be used to move the point where we read from in the SX10509 network receive buffer. Its value for each iteration of the parsing loop will actually equal the number of characters that were "consumed" on the previous iteration. Here's the initialization code, which also clears the LCD:

        OBLCD_Cls
        SetMem          bSCH_RxBufferOffset,0  ;Initialise the offset pointer

Each time we read from the SX10509 we will send it the current value of bSCH_RxBufferOffset. The SX10509 will add that to the previously accumulated read offset to decide where to start reading from. If we send it 0, the SX10509 resets its offset to 0.

The parsing loop

The basic principle of how we handle the returned data is this: Each data item identifies itself with a variable name and an equals sign. So we start at the beginning and test if the stuff at the start of the SX10509 network Rx buffer is a recognizable name. Assuming it is, we extract the value (up to the terminating carriage return or end of the data) and take whatever action is required for that named item. Then we move the starting point in the SX10509 network Rx buffer to skip the characters just processed. We repeat this (loop) until all the data in the SX10509 network Rx buffer has been used up. You should note that we do a fresh read of the SX10509 network Rx buffer into the Xwire Rx data block for each data item, starting from the accumulated offset in the SX10509 network Rx buffer.

The diagram above shows the situation after the first Read command is echoed by the SX10509. The yellow boxes are the 30 bytes that contain data in the Xwire Rx data block in the SPLat. The white on blue text is the contents of the Network Rx buffer in the SX10509 board. The arrow shows where data is moved from. Carriage return characters are represented by pipe symbols |. To the left you can see the read offset that's used in the SX10509 for this read, and the number of bytes reported in bRxLength.

Here's the code that actually reads from the SX10509. It uses the SCH_Read subroutine we developed earlier, with bSCH_RxBufferOffset as an argument, to read from the SX10509. When the read is done it checks that there was something there and terminates the process if there was nothing.

ParseLoop:
        Recall          bSCH_RxBufferOffset
        XtoI
        GoSub           SCH_Read       ;Read from SCH n/w Rx buffer at offset I.
                                       ;Result in Xwire n/w Rx data block
        WaitForST       sSCH_GotEcho   ;Wait for the command to be echoed
        GoIfMZ          bRxLength,T3_NoMoreData  ;Make sure SCH returned something, else quit

After the Read the program tests for a valid variable name at the start of the Xwire Rx data block. It does this using the iiStrFind instruction. iiStrFind was designed specifically for this kind of use. Its documentation explains in detail how it works. This section of code ends in a Branch to the appropriate code for each expected variable name, plus a catch-all if no match is found.

        NVSetPtr        ptrValueNames ;Set the NVEM pointer to the table of string pointers
        NVSetPage       0             ;Playing safe
        LoadI           0             ;Search from the 1st data byte in the Xwire network Rx data block
        iiStrFind       abRxData,kRxDataLen,0  ;Try to find a match in NVEM table of variable names
        Branch
        Target          T3_NoMoreData ;Not found in the table
        Target          T3_Therm
        Target          T3_CkTime
        Target          T3_Pump
        Target          T3_Valve

After the first Read, when the SPLat program checks if there is a recognizable variable name at the start of the Xwire Rx data block, it will find therm=. At the same time I will have been incremented so it "points" to the start of the value (courtesy of the magic of iiStrFind). The program then does a Branch to T3_Therm, the code required to handle the variable therm. This extracts the value (67.78) using a iifGetNum. iifGetNum, increments I by the number of characters processed in extracting the value.

You will notice that iiStrFind and iifGetNum, have both incremented I by the number of characters each "consumed". As we set I to 0 just before the iiStrFind, I will now contain the exact count of the total number of characters consumed in extracting the name-value pair.

A simple subroutine, UpdatePointers, not shown here, takes care of incrementing I to account for the carriage return character after each name-value pair and updating bSCH_RxBufferOffset ready for next time.

T3_Therm:    ;Get the temperature value and display
        iifGetNum       abRxData,kRxDataLen,255 ;Get a float from the Xwire n/w Rx data block
        OBLCD_SetCur    0,0
        OBLCD_Text      "T="
        OBLCD_fDispW    5,2
        GoSub           UpdatePointers
        GoTo            ParseLoop

By the time therm has been processed bSCH_RxBufferOffset will contain 12, the exact number of characters in therm=67.78|.

After the second pass through the parsing loop, the situation is as shown below:

The second Read is performed with bSCH_RxBufferOffset=12, and the Read offset in the SX10509 also at 12. Hence the readout starts at the 13th character in the SX10509, which winds up in the 1st data byte of the Xwire Rx data block in the SPLat. This time, the reported byte count is only 24, as that is the number of characters actually transferred. This time the variable name cktime is extracted, along with its value, 99 (code not shown here). The number of bytes consumed is 10.

The third Read is performed with bSCH_RxBufferOffset=10. This is sent to the SX10509, where it is added to the already existing value of 12, to give a new offset of 22. Hence the readout starts at the 23rd character in the SX10509, which winds up in the 1st data byte of the Xwire Rx data block in the SPLat. This time, the reported byte count is 14 The variable name pump is extracted, along with its value, 0. The number of bytes consumed is 7. The code that handles pump updates a front panel LED. To do this it exploits the fact that ASCII 0 has bit 0 clear, while ASCII 1 has bit 0 set. Hence an AndM with 1 will give 1 or zero, which can be sent to the LED output.

T3_Pump:
        iRecall         abRxData                ;'30 for OFF, '31 for ON
        IncI                                    ;Past the character just used
        LoadX           1
        AndM
        Output          oLED0
        GoSub           UpdatePointers
        GoTo            ParseLoop

The 4th read provides a bit of a change, because it has no terminating carriage return. The SX10509 will actually return '00 bytes on a read, if there is nothing else there. The absence of a carriage return is not a problem.

The fifth read returns nothing, because it is indexed past the end of the data. This is signalled in the bRxLength byte. You may recall that we test for this very condition near the top of the parsing loop.

        GoIfMZ          bRxLength,T3_NoMoreData  ;Make sure SCH returned something


Startup code

;===============================================================================
; This code runs at power on.  It launches all tasks.
;===============================================================================
Start:
        XWireMaster     pXWireConfig                    ;start xwire
        On              20                              ;MS120 (only) backlight
        OBLCD_Text      "SPLat Call Home"
        LaunchTask      ServerUpdate
        RunTasksForever                                 ;go run all tasks

The startup code simply starts up Xwire communications according to the master configuration table in NVEM at pXWireConfig. Then it turns on the LCD backlight (needed only for MS120), displays a splash screen message and launches the one and only MultiTrack task used in this example.


Exception handling

Exception handling is what we include in a program to make it robust enough to withstand things going wrong, be they expected or unexpected. For example, what happens if we send a GET to the server but the server fails to respond? A badly written program might totally lock up. With good exception handling it continues to perform its other chores and pick up on the server communications when restored. Think "graceful degradation".

The sandbox program presented so far has no exception handling. I deliberately left it out to keep the main functions as simple as possible to follow.

The main thing that could go wrong with Internet communications is that the connection is lost, be it for seconds or forever. It can happen at any time on the Internet. Our program should be able to take dropouts in its stride.

One strategy for a program such as this that helps to make it robust, is to have all the Internet stuff in one or more dedicated MultiTrack tasks. Say this sample program is part of your sausage machine controller. If the Internet fails, you still want to make sausages - you may simply lose the remote readout of how many sausages have been produced. If the counting and reporting is in quite separate tasks, a failure of the Internet will not stop production.

Just isolating the Internet stuff in separate tasks is not enough if your application relies on input from the Internet. In that case you may have to be able to detect the Internet failure and provide a default behaviour that can take over. This is called exception trapping (detecting the problem) and exception handling (doing something about it).

One of the most common and versatile ways of detecting something going wrong outside the controller is by a timeout. If the program initiates some action in the real world, and the expected response fails to eventuate in a preset time, something is assumed to be wrong ... there's an exception.

Timeouts are a good way of detecting Internet failures. When we send a GET or POST request to a server, we can reasonably expect a response in less than a second (but more than maybe a few tens of milliseconds). Let's say we allow 5 seconds, to be very conservative. Our sandbox program can be modified to perform this timeout very simply. Remember the semaphore we used to signal that a command had be echoed? It's called sSCH_GotEcho. Let's add a second one, called sSCH_GotException, which is to be set whenever it takes more than 5 seconds to get a response (echo) from a server command. We generate sSCH_GotException by modifying the transitory task tsk_SCH_AckWait. Here's the modified code (new/changed lines are flagged with <<<<<<):

(Click here for some tips for working around problems with copy and paste out of Internet Explorer and HTML-help (.chm) files)

sSCH_GotException       defSEM                ;<<<<<<
tsk_SCH_AckWait:
        ClrS            sSCH_GotException     ;<<<<<<
        MarkTime                              ;<<<<<<
SCH_AckWait_Loop:
        YieldTask
        Recall          bTxCmd                ;Get back the command just set
        Recall          bRxCmdEcho            ;read the Rx echo location
        Compare
        GoIfZ           SCH_AckWait_A         ;g/ the same <<<<<<changed line
        LoopIfTiming    500,SCH_AckWait_Loop  ;<<<<<<
        SetS            sSCH_GotException     ;<<<<<< Signal a timeout
SCH_AckWait_A:
        SetS            sSCH_GotEcho          ;Signal that the echo is received
        KillTask                              ;Job done!

This version will set sSCH_GotEcho whether it has success or failure. It will only set sSCH_GotException if the command timed out.

Now all we have to do is test sSCH_GotException each time we get sSCH_GotEcho, and jump to the code that will do something about the problem. For example, in test 1:

Test1:
        GoSub           SCH_Idle                ;Force the SCH into idle
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed  <<<<<<<<<<or fail
        GoIfST          sSCH_GotException,SX10509Failed                            ;<<<<<<<<<<
        GoSub           SCH_ClearTx             ;Make sure the SX10509 Tx buffer is clear
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed  <<<<<<<<<<or fail
        GoIfST          sSCH_GotException,SX10509Failed                            ;<<<<<<<<<<
        GoSub           SCH_Get                 ;Send a GET request to the server
        WaitForST       sSCH_GotEcho            ;Wait for the command to be echoed  <<<<<<<<<<or fail
        GoIfST          sSCH_GotException,InternetFailed                           ;<<<<<<<<<<

As you can see, we can even discriminate between a failure to communicate with the SX10509 and a failure to contact the server.


SPLat Master to Kaspro Demo

This demonstration program is designed to give you a working example of a SPLat MMi202 communicating with a Kaspro Gemini HMI device. It should provide you with a known, working starting point for your own application. The SPLat is the ModBus Master and the Kaspro is the Slave. With the SPLat as the master it could be controlling other devices via ModBus simultaneously.

We suggest you read through the description below and at the same time familiarize yourself with what the project is doing. Then you can study the code to discover the detailed techniques of what we have done. Once you understand this demonstration it should be easy to modify to suit your own needs.

Note: The Kaspro HMI is a 3rd party product.

What is demonstrated:

  • ModBus communications between a SPLat Master and Kaspro HMI Slave.
  • Storage and recovery of data in SPLat permanent memory.
  • Data transfer between MMi Master and Kaspro Slave.
  • Floating point numbers
  • 16-bit numbers
  • 8-bit numbers
  • Semaphore exchanges both ways
  • ASCII character strings

Previous Topic

Next Topic

In This Chapter

Kaspro: What you will need

Kaspro: Programming

Kaspro: Connecting your controller to the Kaspro

Kaspro: Demo program startup

Kaspro: Configure screen

Kaspro: Operation screens

Kaspro: Maintenance screen


Kaspro: What you will need

  • MMi202 with Firmware V3.24 or later (won't work on older firmware!).
  • Kaspro Gemini HMI (other Kaspro models should work with some adjustment of the Kaspro screen layouts).
  • USB-A to USB-B cable to connect the Kaspro to your PC.
  • RS232 male to female straight through DB9 cable to connect Kaspro to the MMi202.
  • Power for MMi202 (12-24V)
  • Power for Kaspro (24V)
  • SPLat/PC software installed on a PC.
  • Kaspro KA-Software installed on a PC.


Kaspro: Programming

Before you can program your SPLat controller and Kaspro HMI you will need the programming files. The demo software files can be found here.

Download "MMi Master to Kaspro Demo.zip"
  1. Click the link above, download and save the file where you can find it. You will get a zip file with a name "MMi Master to Kaspro Demo.zip"
  2. If your Windows version has zip support enabled, all you need to do is open (double click) the downloaded zip file/folder and drag and drop the folders "KasproDemo_1" and "SplatDemo_1" to a location where you can easily access them.
    Or if you have an archive program installed on your PC you can use that to extract the contents of "MMi Master to Kaspro Demo.zip" to a location where you can easily access them.

Programming the SPLat

Translate and download the SPLat Master Demo program "KasproDemo_1.SPT" into the MMi202 via SPLat/PC. Downloading instructions are in the classic help file.

Programming the Kaspro

Locate the Kaspro Project directory SPLatDemo_1. Inside this directory will be a file called "SPLatDemo_1.wpj". Open it with the KA-Software. Download the Kaspro Slave demo program "SPLatDemo_1.wpj" into the Kaspro via the KA-Software. See the Kaspro manual.


Kaspro: Connecting your controller to the Kaspro

To connect the MMi202 to the Kaspro you will need an RS232 straight through cable. Connect the cable from the "PC COMMS" connector on the MMi202 to the "COM0" connector on the Kaspro.


Kaspro: Demo program startup

Note: Reference is made below to the MMi202 LCD display. It is not essential to have an LCD on the MMi202, but it helps.

At startup there is a 10-second delay before ModBus communications start.

The MMi202 LCD reads '10 seconds to connect to SPLat/PC'
During this time the Kaspro will display an empty text box on the Operation screens and zero values on the configuration screen and elsewhere.

Pretend configuration data for the demo system is stored in the MMi202 permanent memory. After startup this must be sent to the Kaspro, so the user will see correct information in the configuration screen. An initial ModBus script in the MMi202 program transfers this data to the Kaspro and also reads it back from the Kaspro to a separate area of MMi202 RAM. The MMi202 program compares the data sent to the Kaspro with the data received back, and waits until there is a match.

If it is a virgin board the MMi202 will load default configuration values.

The MMi202 LCD reads 'Waiting for HMI to respond'
During this time the Kaspro will still display an empty text box on the Operation screens and zero values on the configuration screen and elsewhere.

When MMi202 receives confirmation that the configuration values have been successfully restored the temporary ModBus script is terminated and the ModBus Master Script is invoked. This runs forever.

The MMi202 LCD reads 'Configuration data restored'
The Kaspro will now show actual data from the MMi202.

ModBus Master script loop

After initialization is complete, the MMi202 starts running its "master script" and you can start to explore the various functions. The Kaspro displays 3 buttons at the top of the display that let you switch between different screens. The four screens are Maintenance, Configure and two Operation screens. Pressing the Operation button multiple times will cycle between the two Operation screens.


Kaspro: Configure screen

Configure screen

You can press on the configuration item and use the pop-up keypad to alter it. The SPLat MMi202 Master pulls the floating point, 16-bit and 8-bit configuration values from the Kaspro, increments them by one then sends them back to the Kaspro to be displayed as the returned and manipulated value. The incrementing is done to demonstrate that the data has gone a "round trip".

The SPLat also stores the modified values in permanent memory.

On entering the Configuration screen the Kaspro sets bit-1 in 'bPermstoreFlag_LSB' and on exiting the configuration screen the Kaspro clears bit-1 in 'bPermstoreFlag_LSB'. This bit is pulled from the Kaspro by the MMi202. A transition from a TRUE state to a FALSE state indicates that the user has exited the configuration screen. The MMi202 then pulls the configuration values from the Kaspro and compares them against the currently stored values in SPLat permanent memory. If any value has changed it stores the new values pulled from the Kaspro into SPLat permanent memory so they can be correctly restored at power-up.

Pressing the 'Flash Error LED?' button on the Kaspro will toggle a status bit inside the Kaspro unit for use with the Status LED on the Operation screen described later.


Kaspro: Operation screens

Operation screens

Pressing the Input 9 push button on the MMi202 toggles bit-0 of 'bCMD_Byte' which is pushed to the Kaspro. This demonstrates error reporting from the SPLat Master to the Kaspro.

In the upper centre of the operation screens is an ASCII Text Display box and a Direct Window component. The Text Display box displays the 'Status Normal' message and is always present. The Direct Window component is located directly on top of the Text Display box, which allows a second Text Display box to be imposed over the first. This second Text Display box is only visible when its Direct Window is activated. It displays the 'Status Error' message. The ASCII status messages originate in MMi202 RAM as a single ASCII string, which is pushed to the Kaspro. Depending on the current state of bit-0 of 'bCMD_Byte' the Kaspro will show or hide the 'Status Error' Text Display box.

In the bottom right corner of the Operation screens is a status LED. This LED is green when bit-0 of 'bCMD_Byte' is FALSE and red when bit-0 of 'bCMD_Byte' is TRUE. There is also a Direct Window component located directly on top of the LED. If the user has pressed the 'Flash Error LED?' button in the Configuration screen the Direct Window will activate and show a second LED imposed on top of the first. This LED is green when bit-0 of 'bCMD_Byte' is FALSE and flashes between green and red when bit-0 of 'bCMD_Byte' is TRUE.

On the left side of both Operation screens are buttons directly relating to the outputs of the MMi202. Pressing these buttons will set corresponding bits 0-7 inside the Kaspro's 'bSPLat_Outputs_LSB' byte. The MMi202 pulls this byte from the Kaspro and updates the SPLat outputs to reflect the state of the bits in the 'bSPLat_Outputs_LSB 'byte.

You can press on the floating point number input item and use the pop-up keypad to alter it. The SPLat MMi202 Master pulls the floating point value from the Kaspro, increments it by one then sends it back to the Kaspro to be displayed as the returned and manipulated value. This demonstrates that the data has gone a "round trip".

Pressing the Input 8 push button on the MMi202 increments an 8-bit counter by one.
The count value is pushed to the Kaspro to be displayed.


Kaspro: Maintenance screen

Maintenance screen

The current SPLat output status is read back and pushed to the Kaspro to be displayed on the SPLat output LEDs on the Kaspro.

The current SPLat input status is read back and pushed to the Kaspro to be displayed on the SPLat input switches on the Kaspro.


Tutorial: Subroutines

Subroutines are an important tool for creating well structured programs. A subroutine is a block of program code that once written can be invoked from several places in a program to perform its function. Think of a subroutine as a re-usable building block.

What this tutorial will teach you (Outcomes)

  • Basic concepts: how subroutines fit into a program
  • The subroutine stack (how SPLatty manages it for you)
  • Do's and don't's, common pitfalls
  • Argument passing

What you need to do or know first (Prerequisites)

Do the online training course (FastTrack tutorial)

Please note: The interactive/online training course is very old. Everything in there is valid and worth learning, especially the underlying principles. However the course pre-dates significant developments, most notably our MultiTrack multitasking technology which makes SPLat programming even easier. So, if you are relatively new to controller programming, the course will give you a good foundation, but please don't think it represents the full SPLat story.

  • An understanding of the register stack. This is covered in the SPLat/PC help file.

Previous Topic

Next Topic

In This Chapter

Subroutines: What are they?

Subroutines: Why use them?

Subroutines: The subroutine stack

Subroutines: Variations on a theme

Subroutines: Passing arguments and results

Subroutines: Style guide


Subroutines: What are they?

A subroutine is a block of code that can be invoked ("called") from anywhere in a program, indeed from many places in a program, and when completed can return control back to the main program. Suppose you have a program where, in several places, you need to turn on a pump if analog input A is more than 39 and the digital input PumpEnable is ON. Otherwise you want the pump off. This code will do it, at least in one place within the program:

          GoIfInOff   PumpEnable,PumpOff
          AnInA
          GoIfXLE     39,PumpOff
          ON          Pump
          GoTo        Continue
PumpOff:  Off         Pump
Continue: ;end of code fragment ... carry on

Let's call this bit of functionality "PumpServe". Imagine we need to do this "thing" in 3 different places in a program. We could achieve that by copying and pasting the same code into 3 places, and incidentally changing the line labels Pumpoff and Continue each time. Alternatively we could make "PumpServe" into a subroutine.

The above drawing illustrates how a subroutine is used. In the main program (left) there are several references (calls) to the subroutine, using GoSub (Go to Subroutine) instructions. Each time the subroutine is called, SPLatty jumps across to the single copy of the PumpServe code, but remembers where he came from. When he is has done the PumpServe operation he returns to the line following the GoSub instruction. This is done with a Return instruction. Hence, a GoSub is similar to a GoTo, but has the added feature of remembering where it came from, so SPLatty can find his way back again. There is a special memory within SPLat called a "subroutine stack", that remembers the return address.

A subroutine may call another subroutine, and that one may in turn call another subroutine. However, you cannot "nest" subroutines indefinitely. The limit is 4 nested subroutines in dialects prior to 12, and 6 in dialect 12 and later.

Let's now re-write the PumpServe function as a subroutine:

PumpServe:
          GoIfInOff   PumpEnable,PumpOff
          AnInA
          GoIfXLE     39,PumpOff
          ON          Pump
          Return
PumpOff:  Off         Pump
          Return

You will notice there is no longer any need to have the GoTo Continue, as it is perfectly OK to have a Return instruction in the middle of the subroutine.

Postscript:

Some programmers maintain that it is bad practice to write a subroutine with multiple return instructions, as I did above. If you are just starting out it may be a moot point. As you gain experience you may agree, on the basis that with only a single exit point from a subroutine there are fewer hazards if you alter the code for some reason. Re-writing the above example in line with this principle, we would get:

PumpServe:
          GoIfInOff   PumpEnable,PumpOff
          AnInA
          GoIfXLE     39,PumpOff
          ON          Pump
          GoTo        PumpRet
PumpOff:  Off         Pump
PumpRet:  Return


Subroutines: Why use them?

There are a number of very good reasons for using subroutines:

Modularity

Using subroutines helps to make your program more modular. That means that in identifying possible subroutines you will be automatically breaking it down into smaller functional units. This helps you to cope with a large task by "divide and conquer".

Code re-use

One subroutine can be called from several places within a program. You may even be able to copy a subroutine from one program and use it intact or with small changes in another.

Incremental debugging

A very powerful technique is to write your program one subroutine at a time, and debug each subroutine before moving on. It is usually easier to debug a single subroutine on its own, because you can preset its inputs more easily, and because you remain focused on a relatively small piece of code.

Better documentation

If you place a descriptive comment at the start of each subroutine, almost like a mini-specification, it will make it so much easier to come along in a few days, months or even years and understand what is going on.


Subroutines: The subroutine stack

Subroutines are made possible by a mechanism called a subroutine stack. The subroutine stack is a "Last-in First-out" memory. This memory stores program memory addresses. When SPLatty executes a GoSub he notes where he is, and sticks the note on top of the stack, before jumping to the new place in the program. If inside that subroutine there is another Gosub, he again notes where he is, places that note on top of the previous one, and goes to the new place. When he encounters a Return he removes the top note off the stack of notes and goes to the place designated on the note. That way he can "unwind" the places he has to return to perfectly.

The stack of return addresses, the subroutine stack, can only contain a limited number of addresses. For dialects prior to dialect 12 the capacity is 4. From dialect 12 the capacity is 6.

Things you shouldn't do (with subroutines, that is)

  • Don't do a GoTo to get out of a subroutine. This is one of the most common "mystery" mistakes I see people making. If you GoSub you must Return.
  • Don't do a GoTo into a subroutine. The trap here is that people sometimes accidentally write GoTo when they meant GoSub.
  • Don't forget the Return instruction (that's the one I do). If I have a really confounding bug I search for missing Returns. It is really hard to see something that's not there!
  • You can use subroutines within MultiTrack tasks. However, you may not yield from inside a subroutine. That means YieldTask, WaitOn, Pause and related yielding instructions are not allowed inside such subroutines. SPLat/PC will flag an error and the controller will suffer a fatal runtime error if you violate this rule.


Subroutines: Variations on a theme

Apart from GoSub and Return, SPLat has a number of other "conditional" GoSub/Return instructions. These will Gosub or not, and Return or not, depending on the outcome of some test. They are all grouped in the Programming reference along with conditional GoTos, so go there to learn about them.


Subroutines: Passing arguments and results

Some subroutines, I dare say most subroutines in SPLat applications, will simply perform some fixed operation without any information passing between the subroutine and the calling program. In some cases, though, you may need some data to pass one way or the other. You may for example have a subroutine that reads a temperature, performing the analog reading and all the necessary calculations as a service to the "caller".

There several ways of passing information back and forth:

  • As data held on the register stack (X, Y, Z and/or T)
  • As data held in the floating point registers W and/or Q
  • As data held in RAM

The information passed may be into the subroutine as arguments, or out of the subroutine as results.

The most common method will be in one of the registers, i.e. X, Y etc or W/Q. Avoid using RAM unless you need to pass more data than will fit in the registers. It is good programming practice to let a given RAM location "belong" only to one subroutine or another (or the top level "main" program), and not be accessible to others. This is an aspect of what is known as data hiding.

One interesting and useful technique is to use X to return an error signal from a subroutine that performs an operation that might go wrong. For example, an industrial cleaning machine may have a detergent pump driven by a subroutine called Squirt. Squirt attempts to dispense one dose of detergent. It returns a logical True value if the operation succeeds and False if it fails (say due to the detergent reservoir being empty). The calling program can the simply call Squirt and test the outcome, rather than having to first test the reservoir level:

      GoSub    Squirt      ;Squirt in a dose of soap
      GoIfF    Failed      ;G/ something went wrong

This might in turn be part of a larger subroutine that carries out a complete sequence. In that case we might make it so it so the larger subroutine returns a zero in X if everything went OK, and a non-zero error number if something went wrong:

Cycle:
      GoSub    Step1         ;Returns False if it fails
      LoadX    Step1FailCode ;In case Step1 failed
      Swap                   ;Get the Step1 result back to X
      RetIfF

      GoSub    Step2         ;Returns False if it fails

      LoadX    Step2FailCode ;In case Step1 failed

      Swap                   ;Get the Step2 result back to X

      RetIfF

... etc ...

      LoadX    0             ;Signal success

      Return

The above subroutine can be called to perform the whole cycle, and when it is done the outcome is in X for evaluation. This may for example be to display a message on the LCD:

      GoSub    Cycle         ;Run a cycle, result code returned in X

      Branch                 ;Test the outcome

      Target   ItWorked      ;Return code = 0

      Target   NoSoap        ;Return code = 1

      Target   OverLoad      ;Return code = 2

      Target   NoFill        ;Return code = 3

ItWorked:

      OBLCD_Cls

      OBLCD_Text  "Cycle complete"

      GoTo     DoTheNextThing

NoSoap:

      OBLCD_Cls

      OBLCD_Text  "No detergent"

      GoTo     DoTheNextThing

.... etc ....


Subroutines: Style guide

When you write subroutines think of it as creating a "black box" functional block that you may use several times in your program. Consider, too, that your program may contain dozens of subroutines, and that you may not actually write the code that calls a subroutine until several days after you write the subroutine. It therefore makes sense to include with the subroutine a brief specification of what it does.

I like to delineate the start of a new subroutine with a visual border, say a row of asterisks. I then preface the subroutine with one or more lines of comments (depending on the complexity of the subroutine) that define its function. Here is an example:

***** ValidCloseCommand ******************
; Return True if a validated close command exists.
; The condition for this is:
;  *  The manual button is pushed OR the failsafe timer has expired
;  *  AND the photo cell is not tripped
;  *  AND the pump is not operating

;Registers:

;  On entry: None

;  On exit : X contains the result

;  Stack   : Clobbers T and Z.

;Subroutine stack:

;  2 levels (calls another subroutine)

ValidCloseCommand:

      GoSub      PumpOperating    ;Get True if yes

      NOT

      Push                        ;Make a copy

      RetIfF                      ;Testing consumes one copy

      Input      PhotoCell        ;False if tripped

      Push                        ;Make a copy

      RetIfF                      ;Testing consumes one copy

      Input      ManualButton

      Test       SomeTimer        ;True if still timing

      NOT                         ;True if timed out

      OR

      Return

*******************************************


Non Volatile Extended Memory (NVEM) User's Guide

This guide covers the NVEM feature in SPLat. You should in the very least skim through the whole document before attempting to use NVEM.

NVEM stands for Non-Volatile Extended Memory. Non-Volatile means this is memory that will not lose its data when the power goes off. Extended means that it exists as an extension to the regular memories in SPLat, which are the Program Memory and the RAM (formerly known as Data Memory).

The NVEM concept provides a framework within which we can implement a very wide range of memory types, ranging from a dozen bytes of battery backed RAM for power-out proofing all the way up to very large (megabyte) memories for data logging applications. All current SPLat products contain a type called "NVEM type 0". It is suited mainly to storing lookup tables within a versatile record structure. Other types may only ever be implemented in custom SPLat controllers.

Note: It is important to keep in mind that there are potentially many different types of NVEM, with different attributes. Not all types will be suitable for all applications, and not all types will exist in all controller models.

NVEM type 0, as provided in current SPLat models, is useful for:

  1. Storing static look-up tables, for example non-linear functions.
  2. Storing dynamically alterable user configuration tables (e.g. configuring a machine for processing different products such as custom processing cycles.)
  3. Storing LCD messages to save memory when the same message is displayed in several different places in your program
  4. Creating an applications specific scripting language and storing scripts in NVEM (this actually was the first live use of NVEM).

Not all SPLat products will contain all possible types of NVEM. In fact, no product prior to MMi200 and SL99 with dialect 15 Firmware contains any NVEM.

We introduced the first NVEM features into MMi200 and SL99 with language dialect 15. We call this type 0 (zero) NVEM, and it has special support in the SPLat/PC programming software (over and beyond the 30+ instructions that apply to all future types of NVEM). With type 0 NVEM SPLat has acquired not only a potentially large (up to 30K) memory for user configuration data, but also a versatile table lookup capability.

Note: NVEM type 0 (NVEM0) makes otherwise unused FLASH memory in the controller available for tables and other uses. It is the nature of FLASH memory that it will wear out after a certain number of writes (reads don't affect it). For that reason the NVEM0 in most controllers is unlikely to be suitable for applications involving frequent writes, such as data logging. Please refer to the documentation for your specific SPLat model for more details.

SL99 NVEM0 details MMi201 NVEM0 details

CAUTION: You must take into account the possibility of power failing right in the middle of writing to any non-volatile memory. Unless specifically stated for the SPLat product you are using, there is always a possibility that data will be corrupted if that happen. You must therefore ensure, by the design of your system, that there will be no disastrous consequences of corrupted data.

The Permanent Memory has the best protection in this regard. Data is written with a checksum, and the PermRecall instruction provides a result indicator in the R register. That means that in the very least your program can have a good probability of avoiding the use of suspect data.

Previous Topic

Next Topic

In This Section

NVEM: Theory of Operation

NVEM: Type specific information

NVEM: Support in SPLat/PC


NVEM: Theory of Operation

This chapter of the NVEM guide is intended to give you a background in the theory of how it works. This is not about chips and wires but about what NVEM consists of and looks like from a programming point of view.

Previous Topic

Next Topic

In This Chapter

NVEM: Programming model


NVEM: Programming model

This section describes how the NVEM appears to a programmer. You need to understand this material before you can start programming. There is a separate section dedicated to NVEM0 programming examples and NVEM0 support in SPLat/PC. The instructions specific to NVEM are in the Programming reference under non-volatile memory.

NVEM is controlled by a repertoire of some 34 SPLat instructions via a set of dedicated registers. These registers can be manipulated in a number of useful ways to simplify programming.

Previous Topic

Next Topic

In This Section

NVEM: Page register

NVEM: Pointer register

NVEM: Record length register

NVEM: Record number register

NVEM: How the registers get used (overview)


NVEM: Page register

The page register selects between up to 256 NVEM pages. Each page will usually be the totality of a particular NVEM type. The default page is 0, and corresponds to selecting the type 0 NVEM. In 99% of applications only page 0 will ever be used, as it is the one page that we expect to exist in all future (post 1Q05) SPLat products.

The remaining registers act on the NVEM within the selected page.


NVEM: Pointer register

The pointer register is a 16-bit register that points to a particular byte address within the selected NVEM page. In initial implementations the maximum value for this register is 32,767 (i.e. we are limited to using the lower 15 bits of a 16-bit register). Hence, the maximum possible page size is 32KB. The pointer register can be used to address all of the page as a single block, but is more often used as a base address when the page is put to multiple uses.

NVEM addresses always start at 0.


NVEM: Record length register

The record length contributes to a scheme that allows you to treat an NVEM page, or a section of a page, as a series of "records". You might visualize one record as equivalent to a row of cells in a spreadsheet. The record length determines how many bytes in total represent one row, or record.

A record can have a minimum length of 1 and a maximum length of 255 bytes.


NVEM: Record number register

The record number register can be set to select a particular record within a section of the selected NVEM page. Imagine this to be equivalent to selecting a row in a spreadsheet. There are instructions provided for setting and modifying the record number in a variety of useful ways.

Records numbers are counted from 0 up, so the first record is record # 0. The largest possible record number is 32,767 (depending on the particular NVEM type).


NVEM: How the registers get used (overview)

Normally you would view a particular page of NVEM as comprising one or more separate tables of data. Each table will consist of several records. A record is like a row in a spreadsheet, and may contain one or more entries. Each entry can be a single byte, a floating point number, a pointer or a text string. A pointer is a 16 bit value that can represent the address of a record in a different table (allowing tables to point into tables, which is getting quite sophisticated!). Text entries can be of fixed length or varying length (which is where tables of pointers come in).

Here is a simple table:

1

4

9

16

If we assume these are byte values, we have here a table with 4 records, each consisting of a 1-byte entry.

Here is a more complicated table:

1 | 1.00000
4 | 1.41421
9 | 1.73205
16 | 2.00000

Each row (record) in this table consists of a byte value and a floating point value. (You may notice that for a number n in the range 1 to 4, the n'th row contains the square and the square root of n.). Because floating point numbers in SPLat take up 4 bytes, each record in this table is 5 bytes long.

Now, in your program you may use several such tables, each with a different number of bytes per entry. These tables will be stored sequentially in NVEM memory (how that happens you will find out about later). In order to access a given entry in a particular record within a specific table your program needs to know the following:

  1. Where in NVEM the table starts (this is the role of the pointer register)
  2. How long each record is (the is the role of the record length register)
  3. Which record you want to access (this is the role of the record number register)
  4. Which entry (square or square root) within the selected record you are after. This is controlled by an offset included in the instruction that reads the table.

The majority of NVEM reads (and writes) therefore calculate the effective address within the NVEM page from the following formula:

Address = Pointer + (RecNum * RecLen) + InstructionOffset + IndexOffset

The IndexOffset is for the indexed mode (of those instructions that support it). It is equal to the number in the index register for byte transfer instructions, and 4 times the number in the index register for floating point transfer instructions.

The number of bytes of data read or written is implied by the instruction itself. There are instructions for accessing single bytes, instructions for accessing pointer values (2 bytes) and instructions accessing floating point numbers (4 bytes). There are also instructions for transferring whole records between RAM and NVEM and between the UV registers (see SPLat expansion framework) and NVEM. There is also a special instruction for transferring text directly from NVEM to the onboard LCD.


NVEM: Type specific information

This section sets out the different types of NVEM and their characteristics. As new types of NVEM are created they will be documented here.

In This Section

NVEM0: Documentation


NVEM0: Documentation

This section covers NVEM type 0.

NVEM type 0 is the type of NVEM that will be found in most, if not all, SPLat controllers starting with language dialect 15 in the MMi200 and the SL99. We will usually refer to it with the short form name NVEM0.

In This Section

NVEM0: Physical description

NVEM0: Size and compatibility

NVEM0: Special support in SPLat/PC

NVEM0: Timing and endurance

NVEM0: Programming examples


NVEM0: Physical description

Physically, NVEM0 consists of "left over" FLASH memory in the SPLat processor chip. The microcontroller chips we use in 2nd generation SPLats like the MMi200 and SL99 contain a FLASH memory. At time of writing the total size of the chip's FLASH memory is 32KB for the SL99 and MS12 and 60KB for the MMi200 (all our code is written in assembler, not C, so we get very good utilization). The FLASH memory contains several things:

  • The "operating system" Firmware that makes the general purpose chip into a SPLat processor.
  • The boot loader that provides the reFlash capability.
  • Permanent memory.
  • Your application program ("SPLatWare")
  • NVEM0

The last two combined are called User FLASH. It's what you get to play with.


NVEM0: Size and compatibility

As we add features to the SPLat processor (such as the 34 instructions that support NVEM in language dialect 15 but did not exist before), the size of the operating system Firmware increases. This means less is left over for SPLatWare and NVEM0. That inevitably means that with future firmware upgrades you will have less memory for yourself in User FLASH. In order to manage this situation, and insure you against suddenly finding your large program won't work, we have instituted the following strategies and policies:

  1. NVEM0 is defined in such a way that it shares the "left over" memory with the SPLatWare memory space. This combined memory is what is called the User FLASH. That means if you have a small program you will have more NVEM available than if you have a large program.
  2. As we introduce firmware upgrades, we will continue to make the reFlash files for earlier versions available. What that means is that if you receive say a dialect 18 board (a hypothetical future dialect) and find that your large dialect 12 program won't fit, you can always reFlash the board back to dialect 12. Your program will still work and your product continues to work as before.
  3. As cost and production factors permit we will move to larger processor chips, making a larger User FLASH possible.


NVEM0: Special support in SPLat/PC

You need SPLat/PC V6.15.0 or later to support NVEM. This page details the special support for NVEM0. This is in addition to the general NVEM support in SPLat/PC.

NVEM0 receives special treatment in SPLat/PC because it can be used for pre-constructed (static) tables. Hence, SPLat/PC needs a way of specifying those tables. This is achieved through a number of "directives" or "pseudo-operations". A directive is something you write in your program that is interpreted by SPLat/PC and used to control what it does. A directive does not result in an executable instruction being loaded into the SPLat board. You are probably already familiar with the directives EQU, mEQU etc.

In your program a directive consists of an optional line label, the directive itself (the keyword) and in most cases an argument. If there is no line label the keyword must start at column 2 or later.

For NVEM0 there are several directives for creating data tables and pointers, plus one for declaring the end of your SPLatWare code and the start of the NVEM storage area.

In each of the following "Label" is an optional name on a line. If used, a subsequent reference to Label will return the NVEM0 address of the line. The directives are:

         NVEM0

This must be used to mark the boundary, within your program source file (.spt file), between executable code and NVEM0. Everything before this must be executable code, and everything after it must be NVEM0 directives. You may only have one NVEM0 in a program.

Label    NV0Byte    b1,b2, ....

Creates one or more bytes of table data with the value b1, b2 etc. The Label is optional.

Label    NV0fNum    ff

Creates 4 bytes of table data containing the floating point number ff. The Label is optional.

Label    NV0Text   "The quick brown fox"

Inserts the quoted text into NVEM. Note that the text string may be any length, and it is stored without any length count or end sentinel. The Label is optional.

Label    NV0Ptr     Lbl

Creates a 2-byte entry that contains the NVEM address corresponding to Lbl. Lbl must exist as a line label in the NVEM0 section of your program. The Label is optional.

SPLat/PC, starting with V6.15.0, includes a window to represent NVEM. It shows the NVEM registers and the data for the selected page. The data display is in hexadecimal and ASCII. The display cannot be manually edited.

NVEM Window in SPLat/PC

Initially the NVEM display will have zero in all registers and 'FF in all data locations. The data display will be initialized with any static table data the first time the simulator executes an instruction that accesses the data. Thereafter it will be initialized whenever you reset the simulator (Run>Reset or CTRL+F7), returning it to a state like a board with a newly downloaded program.


NVEM0: Timing and endurance

NVEM0 is implemented in the main FLASH memory of the SPLat processor. Please refer to the documentation for each specific controller product for details of speed and endurance.

It is important to take into account the speed of the NVEM0 if your program is performing very fast, critical operations. In most cases the NVEM0 will be fast to read but slow to write. While it is being written the processor will be unable to do anything else, so effectively your controller will be asleep for that time.

Endurance is the memory's ability to withstand repeated writes. FLASH memory has a finite life. Once it has performed a certain number of write cycles it becomes worn and is likely to return corrupted data. Reading the data has no effect on life.

At time of writing all foreseeable implementations of NVEM0 will be severely limited in endurance, and will basically be suitable only for very occasional writing (maybe a few hundred times). Check the product-specific documentation carefully!


NVEM0: Programming examples

In this section you will find some examples of programming NVEM0, provided for your delight and edification. You should read them in sequential order, because each one builds on its predecessors.

Previous Topic

Next Topic

In This Section

Example: Simple byte lookup using NVEM0

Example: Simple floating point number lookup using NVEM0

Example: Scan table for >= byte match, get floating point number

Example: Find a substring in an NVEM0 table

Example: Writing NVEM using NVPopByte


Example: Simple byte lookup using NVEM0

This example is for a simple lookup table with 1-byte entries. Given a number in X, we want to read out the X'th byte from the table (which just happens to be a table of prime numbers). Because all addressing starts at 0, X=0 will access the first value in the table, i.e. the number 1, X=1, will access the 2nd entry (3) etc. (which rather neatly side-steps the argument over whether or not 1 is a prime!)

The program fragment is separated into a code part and a table part, with labels Code: and Table:. Notice the NVEM0 directive that marks the end of the code segment and the start of the table (NVEM0) segment.

The table is made up of one line per entry. In this case it's all single byte entries, so we use the NV0Byte directive to define them.

The code is merely a fragment that would exist somewhere in the middle of a larger program, perhaps as a subroutine. The first instruction selects page 0, which is NVEM0. This is needed only if you are switching between different NVEM types, and is included here mainly for show.

The second instruction sets the NVEM pointer to the start of the table. Imagine a program that contains several tables. Setting the pointer ensures that the code is working on the correct table.

The third instruction sets the NVEM record length register. All NVEM read instructions compute the target address from Pointer, Record Length and Record Number, so these must all be correctly set.

The fourth instruction is a little trickier. Remember that the number of the record we want is in X. We need to get that number set in the NVEM Record Number register. The NVPopRecNum instruction achieves just that by copying X to the Record Number register then popping the stack (the register stack is described fully in the SPLat/PC help file).

Finally we use a NVPushByte to read out the data from the NVEM and Push it onto the register stack, i.e. into X. The net effect is that the number in X is replaced by the corresponding prime number. Notice that the NVPushByte has a zero argument. That argument is used to pick out different bytes from multi-byte records.

Code:
    NVSetPage      0       ;Select NVEM0
    NVSetPtr       Table   ;Get to start of table
    NVSetRecLen    1       ;Needed so offset is calculated correctly
    NVPopRecNum            ;Set the record number from X
    NVPushByte     0       ;Get the result to X

     .... continue ...

    NVEM0   ;===== End of program code, start of NVEM0 data tables

Table:

    NV0Byte         1

    NV0Byte         3

    NV0Byte         5

    NV0Byte         7

    NV0Byte         11

    NV0Byte         13

    NV0Byte         17

    NV0Byte         19

    NV0Byte         23

    NV0Byte         31

    NV0Byte         37

    NV0Byte         41

    NV0Byte         43

    NV0Byte         47


Example: Simple floating point number lookup using NVEM0

This example is for a lookup table with single floating point entries. Given a number in X, we want to read out the X'th byte from the table (which just happens to be a table of sines, where X is in degrees). Because all addressing starts at 0, X=0 will access the first value in the table, i.e. the number 1, X=1, will access the 2nd entry (3) etc.

The table uses NV0fNum directives, one per line, with floating point arguments. Each entry takes 4 bytes, the amount of memory used by SPLat for floating point numbers.

The code is very similar to the previous example, that extracts single-byte table entries. Note though that the record length is set to 4. The required record is otherwise targeted in the same way, by setting the pointer and the record length and popping the record number.

A floating point number cannot be held in X so we use the NVfReadW instruction to fetch the data from the table and place it in the floating point register W. NVfReadW has an argument, in this case 0, that specifies an offset in bytes into a record.

Code:
    NVSetPtr       Table   ;Get to start of table
    NVSetRecLen    4       ;Needed so offset is calculated correctly
    NVPopRecNum            ;Set the record number
    NVfReadW       0       ;Get the result

     .... continue ...

    NVEM0   ;===== End of code, Start of NVEM0 data

table:

    NV0fNum         0.0

    NV0fNum         1.74524E-2

    NV0fNum         3.48995E-2

    NV0fNum         5.23360E-2

    NV0fNum         6.97565E-2

    NV0fNum         8.71557E-2


Example: Scan table for >= byte match, get floating point number

This example is a little more interesting. Instead of using X to directly select a table record (row), it scans the table for a "greater than or equals" match with X and then fetches the corresponding floating point number. Here is the data table in "spreadsheet" form, one row per record.

5 | 12.0
89 | 78.9
127 | -98
255 | 0.0

The left hand column contains byte entries which will be matched for >= X. The right hand column contains the corresponding floating point "answers". The object of the exercise is to scan down the lefthand column for the first entry that is greater than or equal to X, then return the floating point number next to it.

You will notice that the last entry has 255 as its "X" value. That is the largest number that can ever be contained in X, and so our scan of the lefthand column is guaranteed to terminate on that entry, if not before.

The code for this example is a bit more elaborate than the previous examples. It initializes the NVEM registers, scans the table in a loop and finally retrieves the resulting floating point number.

The first 3 instructions initialize the NVEM registers. This includes setting the record number to 0, because we are going to scan the table from the top.

Within the loop we first access the byte entry from the record number currently selected by the record number register. Pushing the table byte into X pushes the original X value down the stack into Y. We then compare the 2 numbers in X and Y using a CompareR, leaving a result in the R (for Result) register. The BranchR instruction gives a (in this case) 3-way branch (split) on the contents of R, with the 3 Target lines nominating where to Branch to for each of the 3 possible outcomes.

If the table byte is smaller than the original X value, we wind up at the program line Smaller. There we increment the record number and go back to Loop to try the next record.

If the table byte is greater than or equal to the original X value, we wind up at the program line Equal or Bigger. There we read the floating point number from the record into W.

Code:
    NVSetPtr       Table
    NVSetRecNum    0
    NVSetRecLen    5
Loop
    NVPushByte     0       ;Get the byte
    CompareR               ;Compare with X (now in Y)
    Pop                    ;clear off the byte
    BranchR
    Target         Equal   ;Byte = original X
    Target         Bigger  ;Byte > original X
    Target         Smaller ;Byte < original X
Smaller
    NVIncRecNum            ;to the next record
    GoTo           Loop

;Found

Equal

Bigger

    NVfReadW       1       ;Offset by 1 to skip the byte value

;..... Done .....

    NVEM0  ;=========  End of code, start of NVEM0 data

Table:

    NV0Byte         5       ;1st record

    NV0fNum         12.0

    NV0Byte         89      ;2nd record

    NV0fNum         78.9

    NV0Byte         127     ;n'th record

    NV0fNum         -98

    NV0Byte         255     ;Sentinel record

    NV0fNum         0.0


Example: Find a substring in an NVEM0 table

This example is a subroutine that uses an NVEM0 table to translate an integer (in X) in the range 1 to 12 into a 3-letter calendar month name and display it on the onboard LCD at the current cursor position.

The program fragment uses the OBLCD_NVText instruction. This instruction transfers RecLen bytes from NVEM to the onboard LCD.

DispMonth:
          DecX                 ;Need the month index to be 0-11, not 1-12
          NVSetPage    0       ;Make sure NVEM0 is selected
          NVPopRecNum          ;Month number, 0-11
          NVSetRecLen  3       ;3 letters per month name
          NVSetPtr     Months  ;Point to the month table
          OBLCD_NVText 0
          Return

          NVEM0        ;End of code, start of NVEM0 memory

Months:   NV0Byte      "JanFebMarAprMayJunJulAugSepOctNovDec"


Example: Writing NVEM using NVPopByte

This example is a stand-alone program that demonstrates writing to NVEM0 using the NVPopByte instruction.

Note: Due to limited endurance of the flash memory, writing NVEM0 is useful only for very infrequently updated data. For most SPLat boards, more than 10,000 individual NVPopByte operations may wear out the flash memory and void the warranty on the controller.

;Example of writing to NVEM using NVPopByte. This program writes incrementing counts to 3 contiguous MNVEM bytes,

;then reads them back to RAM so they can be viewed via SPLat/PC using SPLatLink ("S/L" button in the Module window)

        nvSetPtr        MyRec       ;Set the pointer to a pretend data base

        nvSetReclen     3           ;Set the pretend record length

        nvSetRecNum     1           ;Use one fixed record number

        LaunchTask      WriteNV     ;Write stuff

        LaunchTask      ReadNV      ;Read back stuff

        RunTasksForever

;===========================================================================================================

;Task that continuously writes NVEM. It stores 3 incrementing numbers at a certain

;non-zero location in NVEM, running once per second.

;To prevent excessive wear on the NVEM it deliberately stops writing after 60 cycles (180 writes, over 1 minute).

;Be aware that the nominal lifetime of NVEM in most SPLat boards is 10,000 writes, so don't

;run this program too many times or disable the counter. (The warranty does not cover worn out NVEM!!!!)

WrData  defByte

WrCount defByte

WriteNV:

        SetMem          WrCount,60

WrLoop:

        Recall          WrData

        Push

        nvpopbyte       0           ;Store a value

        IncX                        ;increment the value

        Push

        nvpopbyte       1           ;Store a value

        IncX                        ;increment the value

        Push

        nvpopbyte       2           ;Store a value

        Store           WrData

        Pause           100         ;Delay 1S between writes

        DMGNZ            WrCount,WrLoop  ;Only do 256 writes before crashing, to protect Flash from wear-out

WrKill: YieldTask

        GoTo            WrKill    ;Deliberately stop writing. Program must be restarted to write again.

;===========================================================================================================

;This task reads out the NVMem that's written by WriteNV, and stores in in ordinary RAM in locations 0-2.

;From there it can be viewed in SPLat/PC using the SPLatLink monitoring accessed through the

;Module window ("S/L")

ReadNV:

        Pause           1

        NVReadRec       0       ;Read back the NVEM record, as NVRecLen bytes, to RAM 0

        GoTo            ReadNV

;=============== NVEM declarations ========================================

        NVEM0

        NV0Space        512        ;NVEM may not be written within one block length of the end of the program.

                                   ;The block length is 512 bytes for most models. This line wastes that much space, unavoidably

MyRec   NV0Space        200        ;Arbitrary (in this case) allocation to the writable data.


NVEM: Support in SPLat/PC

SPLat/PC now includes a window to represent NVEM. It shows the NVEM registers and the data for the selected page. The data display is in hexadecimal and ASCII. The display cannot be manually edited.

NVEM Window in SPLat/PC

Initially the NVEM display will have zero in all registers and 'FF in all data locations. The data display will be initialized with any static table data the first time the simulator executes an instruction that accesses the data. Thereafter it will be initialized whenever you reset the simulator (Run>Reset or CTRL+F7), returning it to a state like a board with a newly downloaded program.

In addition to this, there is special support for NVEM0 in SPLat/PC.


RealTime Clock

The RealTime Clock function in SPLat provides a comprehensive set of features to allow you to build controller application that are aware of and react to the time of day and the date. Features of the RTC include:

  • An accurate hardware calendar/clock
  • A number of programmable Events that can be set to trigger actions at certain times of day on certain days of the week. This corresponds to a 7-day time clock with multiple outputs.
  • A number of program instructions to manipulate time, date and event values.
  • Built in display functions to simplify programming
  • A comprehensive user interface library.

This documentation is organized as two major sections. The first section describes the workings of the RTC in terms of the hardware and how the various part relate to each other. The second section is a programming guide. You need to have a general understanding of the first before you can understand the second.

Previous Topic

Next Topic

In This Chapter

RTC: How it works

RTC: Programming

RTC Instructions


RTC: How it works

This section describes the various hardware aspects of the RTC.

What is described is in reality the programming model, which may or may not correspond to the physical hardware. The programming model is what your SPLat program sees and has to handle. You should also look at the RTC documentation for the specific controller you are using.

The RTC consists internally of a number of registers for hours, minutes, seconds, year, month etc. There are also a number of registers associated with the events. Rather than make this large number of registers individually addressable, we have created a a number of shared registers that the RTC can be read into and written from. Thus, to set the time, you read the time from the RTC into the registers HH and MM, alter those registers and when done write them back to the RTC time. The very same HH and MM registers are used to set events. Hence, the same routine can easily be used to set the time of day and to set event times.

An event comprises a set of registers that store the hour and the minute when the event is to trigger, plus a 7-bit mask that contains one bit per day of the week. An event triggers when the event hours and minutes match the current time and the current day is represented by a bit set in the corresponding position in the day mask. An event can only be tested for true (triggered) during the exact minute it is set for, and can only be read out as true once per day.

In This Section

RTC: The clock

RTC: Handling days

RTC: Processor registers

RTC: The calendar

RTC: The events

RTC: Backup power

RTC: Memory wear-out


RTC: The clock

The clock (or Time of Day clock) is a 24-hour clock. You can read out hours, minutes and seconds. You can set hours and minutes, but not seconds. When you set the time the seconds are forced to 0. Hours are always 0-23, i.e. there is no am/pm indicator.


RTC: Handling days

The day has three separate representations: The day of the week (Monday to Sunday) exists in 2 different formats depending on use, and there is also day of the month (1st through 31st). The RTC handles these day of week and day of month quite separately, and makes no automatic connections between them. It does however know how many days there are in each month, and it knows about leap years.

Day Number (DN)

In the clock there is a day number (DN) that counts in the sequence 0-1-2-3-4-5-6-0 at midnight. The 7 values correspond to the 7 days of the week. Monday is day 0. DN is read out with the time and written separately.

In the EC1 the RTC hardware calculated Day Number from the date, so DN is read-only.

Day Mask (DM)

An event can be set to trigger at a set time of day on any combination of days of the week. Each event contains a Day Mask (DM). A day mask is a byte of data where bits 0 to 6 each correspond to one day. By manipulating the individual bits in a day mask you can determine which days of the week the event will activate on. A bit set (1, True) activates that day. Day masks are read and written as part of an event.

Day of month

Within the calendar function the day of month is updated each midnight and maintained correctly in terms of the number of days in the month. The day of month is read and written with the date.


RTC: Processor registers

The RTC function adds a number of registers to the SPLat controller. These registers are used to transfer data to and from the various parts of the RTC itself, and are thus shared across a number of functions.

The registers are HH, MM, SS, DN and DM.

HH register (shared with year)

The HH register contains hours when the time or an event is read and the year when the date is read.

The hour is a number in the range 0-23. The year is a number in the range 0-99, representing 2000 through 2099.

MM register (shared with month)

The MM register contains minutes when the time or an event is read and the month when the date is read.

Minutes is a number in the range 0-59. Month is a number in the range 1-12.

SS register (shared with day of month)

The SS register contains seconds when the time or an event is read and the day of month when the date is read.

Seconds is a number in the range 0-59. Day of month is a number in the range 1-31.

DN register

The DN register contains the day of week as a number 0 to 6 when the time or date is read.

DM register

When the time is read the DM register is set so that just the bit that corresponds to DN is set. When an event is read the DM register reflects the days for which the event is set. A bit set (1, True) activates the event for the corresponding day.When the date is read all bits of the DM register are set.


RTC: The calendar

The calendar can be read and set. It tracks the date with leap year correction. The design range is years 2000 through 2099.


RTC: The events

Events are moments in time that can be used to trigger actions in your program. They allow functionality similar to that provided by a 7-day time clock.

Each event can be set to trigger at a particular time of day on any combination of the days of the week. Each event consists of an hour, a minute and a day mask.

The events behave in such a way that an event can only be read out as True (tripped) once per matching time and day. Also, it must be read out during the exact minute it trips, or it will be missed. For example, if an event is set for 14:34 each Monday and Tuesday, then:

  • If tested at 14:34 on Sunday it will be False (not set for Sunday)
  • If tested at 14:33 on Monday it will be false (too early)
  • If then tested at 14:34:05 on Monday it will be True (first test during the "golden minute").
  • If then tested at 14:34:25 on Monday it will be False (it has already be tripped and read out today).
  • If then tested at 14:33:59 on Tuesday it will be false (too early)
  • If then not tested again until 14:35:00 on Tuesday it will be false (it was not tested during the "golden minute", so it missed out entirely on Tuesday).

The event number will be jndexed when executed inside a MultiTrack task and will be indexed if the instruction is preceded by the IasJ: precode. This can be extremely useful for multi-channel applications, say controlling 4 zones of lighting.

Please refer to the product documentation for your particular controller to find out how many events it supports.


RTC: Backup power

Real Time Clocks are frequently equipped with a battery or other form of power source to maintain the clock (time) and data like event settings while the controller is without power. Please consult the RTC documentation for your specific SPLat controller product for details of backup power. Most commonly we use a large capacitor (0.1F), which provides 2 days or more of backup and has none of the lifetime limits of batteries.


RTC: Memory wear-out

The memory used to store RTC events may have a finite write cycle endurance. That means executing too many RTCWriteEvent instructions may damage it. This is documented under individual products.


RTC: Programming

Programming the RTC has two aspects:

  • Setting the various RTC data (time, date events) (User Interface) and
  • Reading out and using them for something useful (typically event triggered control actions).

This section covers both aspects.

In This Section

RTC: Support in SPLat/PC

RTC: Display instructions

RTC: Using Events

RTC: User interface library


RTC: Support in SPLat/PC

The bottom part of the SPLat/PC register display shown above represents the RTC. The items are, left to right, top to bottom:

HH:MM:SS register (05:10:00)

The processor registers HH, MM and SS are shown in a single box. They are separated by colons when displaying the time or an event, and by forward slashes when representing a date. When the simulator initially reads the time or the date, it will get the PC's system clock. Thereafter it is possible for your SPLat program to change the time and date and write it back to the simulated clock, without affecting the PC's clock. The simulated (SPLat/PC) will then keep its own time.

Day Number (DN)

This contains a day number in the range 0-6. It can be set and manipulated in several ways. It is often used to select one day in the Day Mask for manipulation, i.e. it acts as a pointer.

Day Mask (-tw-f_-)

Day Mask information is show in a special format that has one character per day. The 7 days are represented, starting from Monday. Only one day can be selected. The selected day can be turned on or off. If a day is ON, it is represented by the first letter of its name. If it is OFF it is represented by a hyphen or an underscore. The selected day is represented by its upper case first letter if ON or by an underscore if OFF. The following table shows all the possibilities:

 | ON | OFF
Selected | Upper case 1st letter | Underscore
Not selected | Lower case first letter | Hyphen

For example, in the screen shot Saturday is selected (underscore) but OFF. If it was turned on the display would become

-tw-fS-

Event trips

The 8 check boxes at the bottom represent 8 events. Note that different controller products may support differing numbers of events. The SPLat/PC simulator does not "trip" simulated events when the time and day mask criteria are met. That would be impractical as a debugging tool. Instead, you can simulate a tripped event by checking (clicking) its checkbox. The event will then read out as True (tripped) the next time the program being simulated tests it.

Note that the displays described above represent the processor registers that are used to interact with the RTC. This is not the same as the RTC itself.


RTC: Display instructions

There are a number of instructions for displaying RTC information on the OnBoard LCD. They are:

OBLCD_HHMM

Display contents of HH and MM as time HH:MM at the current cursor

OBLCD_HHMMSS

Display contents of HH, MM and SS as time HH:MM:SS at the current cursor

OBLCD_Date

Display contents of HH, MM and SS as a date YY/MM/DD at the current cursor

OBLCD_DM

Display the day mask as for example m-Wt--s with an upper case letter (enabled) or underscore (disabled) at the Day Number (DN) position


RTC: Using Events

The basic principle of using events to trigger actions in your program is that you can test each of the events to determine if it is tripped. An event is tripped if:

  • The event is armed. An event gets armed by an RTCWriteEvent or by being tested outside the "golden minute" (the actual minute the event is set for), AND
  • The current minute (now) is the same as the minute for which the event is set, AND
  • The current day is a day for which the event's Day mask is set, AND
  • The event has not already been read as tripped today (except altering event settings re-arms it).

There are 2 instructions for configuring events:

RTCReadEvent n

Reads event register n to the HH, MM and DM registers for display and/or modification

RTCWriteEvent n

Writes HH, MM and DM to event n after modifications

There are 3 instructions for testing events. These are:

GoIfRTCEvent n,Line

If event n has tripped, goes to the named program Line.

WaitForRTCEvent n

Waits for event n to occur. This can only be used in a MultiTrack task.

RTCTestEvent n

If event n has tripped, pushes True into X. If the event has not tripped pushes False into X.

Note that the event number in all cases is subject to IasJ: precode and, if used inside a MultiTrack task, jndexing.

Please refer to the product documentation for your particular controller to find out how many events it supports.

Example:

In an energy management system for a 24-7 food store, the outside lights are to be turned on according to the following monthly schedule:

Month | On at | Off at
Jan | 1700 | 0800
Feb | 1730 | 0730
Mar | 1800 | 0700
Apr | 1830 | 0700
May | 1900 | 0630
Jun | 1900 | 0600
Jul | 2000 | 0600
Aug | 2000 | 0530
Sep | 1900 | 0600
Oct | 1800 | 0630
Nov | 1730 | 0700
Dec | 1700 | 0730

The program has 3 MultiTrack tasks. The actual turning on and turning off of lights is done by LightsOn and LightsOff. These are simple "3-liners" that simply wait for their respective events then perform their action.

The turnon and turnoff events are maintained by a task called UpdateTimes. UpdateTimes is more interesting. It pre-loads events 0 and 1 according to a schedule stored as a table in NVEM page 0. UpdateTimes is itself run once a day using event 7, which it presets itself to trip at midnight, when first launched.

It is not actually necessary for the times to be refreshed every midnight. If they are set at start-up it will be enough. However by doing it this way we open the door to a system that can have the times changed either through a built in user interface, or by a remote communications facility. Testing events repeatedly in a looping MultiTrack task, like this example does, is a good way of meeting the requirement that an event needs to be tested outside the "golden minute" in order to be re-armed.

OutSideLights   oEQU    0

                LaunchTask              UpdateTimes

                LaunchTask              LightsOn

                LaunchTask              LightsOff

                RunTasksForever

;Task to turn on the lights at the right time each day

LightsOn:

                WaitForRTCEvent         0

                On                      OutSideLights

                GoTo                    LightsOn

;Task to turn off the lights at the right time each day

LightsOff:

                WaitForRTCEvent         1

                Off                     OutSideLights

                GoTo                    LightsOff

;Task that keeps the turnon and turnoff event times updated

UpdateTimes:

                GoSub                   SetEvents       ;Initialize the events

                LoadX                   23              ;Preset event 7 to midnight every day

                RTCPopHH

                LoadX                   59

                RTCPopMM

                LoadX                   255

                RTCPopDM

                RTCWriteEvent           7

UpDateTLoop:

                WaitForRTCEvent         7               ;Wait for midnight

                GoSub                   SetEvents       ;Refresh the events

                GoTo                    UpDateTLoop

SetEvents:

        ;Refresh the turnon and turnoff events

                NVSetPage               0               ;Select NVEM0 page

                NVSetPtr                OLTable         ;Point to table of on/off times

                NVSetRecLen             4               ;4 bytes per table entry

                RTCReadDate                             ;Get the date

                RTCPushMM                               ;Access the month

                DecX                                    ;Months are 1-12, table is 0-11

                NVPopRecNum                             ;Point to table entry

                RTCReadEvent            0               ;Turn on time

                NVPushByte              0               ;Turn on hour

                RTCPopHH                                ;Store Turn on hour

                NVPushByte              1               ;Turn on minute

                RTCPopHH                                ;Store Turn on minute

                RTCWriteEvent           0               ;Save updated event

                RTCReadEvent            1               ;Turn off time

                NVPushByte              2               ;Turn off hour

                RTCPopHH                                ;Store Turn off hour

                NVPushByte              3               ;Turn off minute

                RTCPopHH                                ;Store Turn off minute

                RTCWriteEvent           1               ;Save updated event

                Return

;The following must be placed at the end of the program, after all executable code:

                NVEM0

;Table of turn-on and turnoff times. Each entry is 4 bytes,

;being HH(on), MM(on), HH(off) and MM(off)

OLTable:

                NV0Byte 17,00,08,00             ;Jan

                NV0Byte 17,30,07,30             ;Feb

                NV0Byte 18,00,07,00             ;Mar

                NV0Byte 18,30,07,00             ;Apr

                NV0Byte 19,00,06,30             ;May

                NV0Byte 19,00,06,00             ;Jun

                NV0Byte 20,00,06,00             ;Jul

                NV0Byte 20,00,05,30             ;Aug

                NV0Byte 19,00,06,00             ;Sep

                NV0Byte 18,00,06,30             ;Oct

                NV0Byte 17,30,07,00             ;Nov

                NV0Byte 17,00,07,30             ;Dec


RTC: User interface library

On our website in the File Resources area you will find a collection of program examples. In there the folder RTC contains a program RTCUILIB.SPT. This program is a complete library of user interface functions. These allows setting of Time, Date, Day of week, and Events using push buttons and the LCD. There are very detailed instructions embedded in the file. The best way of getting up and running with a user interface for the RTC is to use this library. If you want something to work differently, start with the library and modify it to your needs.

Note: The library is written to be used in a conjunction with MultiTrack.


Tutorial: PID control with SPLat

Welcome to our tutorial on practical PID control using SPLat.

What you will get out of it

The purpose of this material is to help you implement a PID control function within your SPLat controller as simply as possible. I will keep it result-oriented, so the approach will be pragmatic and as non-mathematical as I can make it. Do keep in mind though, that this is a complex subject that people make whole careers out of, and for which there is no clear-cut, definitive set of solutions. At the end of the day it comes down to developing something of a gut feel and maybe even getting a bit lucky!

On the face of it, it may look as if there is an excessive amount of material to plow through here "just" to be able to control something with PID. Trust me on this: It is very easy to get lost in the PID wilderness. Unless you have a lot of prior experience with PID you could save literally days, maybe weeks, by using this systematic, structured PID roadmap and the tools that come with it.

What you will need

To use this material fully you will need:

  • The PIDassist program installed in your computer. PIDassist is available free on our website, if you don't already have a CD with it (it may pay to check the website, anyhow, to make sure you have the latest version). When you install PIDassist you also get several SPLat support programs and Excel spreadsheets. You can find PIDAssist in the downloads section of our website.
  • A SPLat controller with analog capability. The very best choice in an MMi200DK216 (or MMi99DK216), because I use the LCD display in several programs. Naturally you need it hooked up to a power supply and your computer.
  • Access to the process you wish to control. Note: The structure of this material and the procedures set out are suitable only for work on a process that you can fiddle with without doing any damage. They are not suitable for in situ tuning of process plants. So, you can use this if you are developing a temperature controlled biofuel digester, but not if you are controlling an aquarium that's already full of fish.
  • Excel or a equivalent spreadsheet program. If you don't have Excel, you can download a free office suite called OpenOffice from www.openoffice.org. This contains a spreadsheet program that will do some of what Excel can do (It doesn't do macros and VBA code like Excel, so you will miss out on some of the functionality).
  • The motivation to learn and a willingness to knuckle down and work systematically and studiously through the material.

Skills prerequisites

As a rough guide this material will work best for you if you have the following skills:

  • Reasonable proficiency with SPLat programming. You need to have gone past FastTrack and have an understanding of the floating point instructions, subroutines and analog I/O. You should be fully up to speed with the SPLat/PC programming software, and know how to connect to a board, translate and download programs to the board.
  • Reasonable proficiency with Excel, including graphing, absolute and relative cell references and block copying and pasting. I make a lot of use of Excel in this material.
  • Knowledge of the system, product or process that you wish to control.
  • The right electrical/electronic skills to interface your process to a SPLat.

    David Stonier-Gibson

    SPLat Controls Pty Ltd

Acknowledgement

My sincerest thanks to my friend Bruce Varley for his constructive inputs to this tutorial. Bruce has been a senior plant engineer in process plants world wide, and has probably forgotten more about PID control than I will ever know. Thanks, mate!

Please note that this material is educational in nature and is not intended as professional advice. It is up to you to apply this material intelligently, to do very extensive testing on any system you create and to seek competent advice where appropriate. You are responsible for the outcomes.

Previous Topic

Next Topic

In This Chapter

PID control: Overview

PID control: Theory

PID control: Using PIDassist

PID control: Implementation

PID control: SPLat programming


PID control: Overview

This chapter will give you a broad-brush overview of PID control

Previous Topic

Next Topic

In This Chapter

PID overview: About closed loop control

PID overview: The PIDassist program

PID overview: The overall strategy

PID control: What SPLat can do


PID overview: About closed loop control

The underlying principle of PID control is feedback or closed loop control. PID is only one of many possible forms of feedback control. PID is not always the best form of control. You need to have a good understanding of the process you wish to control before you can decide if PID control is the appropriate choice. Closed loop control in-principle diagram

The Process is some apparatus that produces an Output that can be varied by changing an Control Input, which is also referred to as a forcing function. The output is continuously variable, like the light from a bulb controlled by a dimmer, as distinct from discontinuous like the light from a bulb controlled by an on/off switch. The input to the process need not be continuously adjustable, but usually is. Notice that in the case of a light bulb example the process consists of the dimmer or switch plus the bulb, and its output is the light produced. (To be more precise, the process is the conversion of electricity into light. The light bulb and dimmer/switch are the apparatus that performs the process.)

The Controller is a device (in our case a SPLat controller) that measures the output of the process, compares it with the desired output (called the Setpoint) and then applies a corrected Control Input (the forcing function) to the process.

This setup is called feedback control because (a measure of) the process output is fed back into the controller and hence (after some number crunching) back into the process. It is also called closed loop control, because the process and the controller are connected together "head to tail" into a closed loop.

Examples of closed loop control:

  • A room thermostat (which by the way is normally not a PID controller).
  • A motor car cruise control.
  • You and your car, as you maintain a constant speed on the freeway. YOU are the controller in this case, you might like to keep this in mind as you gain understanding of the mechanisms behind automatic control.
  • A petroleum refinery (which actually has hundreds of interconnected control loops)

Notice that the output of the process (or at least a measure of it) becomes an input to the controller, and the output of the controller becomes the control input to the process.

The controller has a second input, the setpoint, also referred to as a Target Value. The setpoint is what tells the controller the desired value of the process output.

The process usually has one or more additional inputs. These are things like electric power, chemical feedstock etc. They do not directly control the output but they can certainly influence it. For example, if the supply voltage drops the light output of the bulb will drop. Often, too, the amount of loading on the output may affect the level of the output. For example, a sudden head wind will slow down your car and result in a higher throttle setting being needed to maintain the same speed.

The purpose of closed loop control is to maintain the process output at the setpoint irrespective of external influences on the process such as mains voltage, loading, ambient temperature etc.


PID overview: The PIDassist program

This is also a manual for our PIDassist program. PIDassist is a computer program that can be used as a process simulator, as a control console for a SPLat running a PID control program and as a code generator for a final SPLat PID program. What this means is that you have available to you a comprehensive set of tools for incorporating PID control into your SPLat application, and you won't have to write any actual PID code or understand very much of the maths. When you install PIDassist on your computer you also get template SPLat programs and a number of Excel spreadsheets to help you both with learning some theory and with practical implementation.

pidassist composite screen shot

You can download the PIDassist package free from the downloads section of our website.


PID overview: The overall strategy

The general strategy we have for implementing a good PID control solution is as follows:

  1. Learn enough about PID control, at an intuitive level with a minimum of math, to understand what makes a closed loop PID control system tick.
  2. Perform measurements on the process you want to control, say a heating system, and analyse the numbers to determine its major characteristics.
  3. Simulate the process and a PID controller using the PIDassist program, and tune the simulated PID control for best performance.
  4. Use a SPLat controller, running our pre-written PID program, to control the real process. This is done using the settings found in the previous step, and uses PIDassist to fine tune the settings. Preferably you will use an MMi200DK216 (or its predecessor the MMi99DK216), as this whole tutorial is written around it. Later you can substitute your own choice of SPLat, maybe a custom unit we are making for you, as long as it has the right hardware resources.
  5. Once the system is tuned up, cut the connection with the PC and run the program standalone in the SPLat.
  6. If applicable, move the program over into the SPLat you intend to use for production, and integrate the PID function into your overall application (it is perfectly possible for the PID to be just one small aspect of a larger program).

However, before we get going, you need to be aware that not all closed loop PID implementations turn out to be straightforward, and without experience it may not be easy to tell whether your project will be straightforward or not. If your application is one where success is critical, you would be well advised to get someone with suitable expertise to check out your initial concept, and maybe your final design.


PID control: What SPLat can do

This is a brief discussion of the capabilities and limitations of using SPLat for PID control. Every system has some limits, and SPLat (as wonderful as we think it is!) is no exception. If you start out with some idea of where the limits lie you may save yourself a lot of grief later.

The practical limits for second generation SPLat boards (like SL99 and MMi99/MMi200) used for PID control are 10mS sampling time and 256 step (8-bit) analog resolution. In practice that means you can control processes with response times down to perhaps 100mS (10 sample times), and for which you can tolerate an overall accuracy of 1 part in 256, or 0.4%. The actual PID calculation takes about 3.5mS, so with a 10mS sampling time you will be using up 35% of the controller's processing capacity. The boiler plate PID control program we provide you with uses up to 32 bytes of RAM, or 17% of what's available in an MMi99 with V3.18 or later Firmware.

Since the above paragraph was written we have introduced MM202 and other controllers with 10-bit analog resolution (1024 steps). PID Assist has not been updated to accommodate that improvement. The code auto-generated by PID Assist can exploit it. The newer controllers are also faster, though we haven't benchmarked the PID code.

PID control is about controlling continuous processes, that is to say something that is continuously and smoothly varying. SPLat is a digital controller that does things in discrete, time-sequential steps. That means that the basic nature of SPLat, or any other digital controller, is basically at odds with the continuous nature of PID control.

The way this conflict is resolved is that the digital device does its work in "lumps", as is its nature, but does so many "lumps" per second that to the outside world it gives the illusion of being continuous. This is called sampling. A PID control implemented with a digital controller is called a "sampled" system.

It is important in a sampled system that the sampling rate, i.e. the number of samples per second, is high compared to the rate at which the controlled process can change. If the controlled process can significantly change its output in 1mS, but the controller can only sample every 10mS, then the controller has little chance of exerting effective control over the system. On the other hand, if the process can only change significantly on a time scale of seconds, minutes or hours, then a sampled control system with a 10mS sampling interval can work (you may even be able to use a lower sampling rate and hence use less of the controller's time).

When using a digital system for PID control, the controller must convert the analog output of the process into a digital number before it can do any sums on it. This requires an analog to digital converter. The analog inputs on SPLat controllers use such a converter.

All analog to digital converters converts an analog input voltage, which can have an infinite number of possible values, into one of a finite range of numbers. The converters in most SPLat boards break down the 0-10V input voltage range into 256 steps, numbered 0-255. That means each number represents an input voltage range of 10/256 volts, or about 39mV. Thus, if the converter returns the number 45, it means the input voltage is somewhere between 44.5 and 45.5 times 39mV, or 1.736V to 1.775V, but where in that range it actually lies is unknown.

A similar conversion, from digital back to analog, takes place on the output of the controller. This too has a finite number of steps, which in the case of SPLat is 256.

Breaking down the voltage (or current, or temperature, or pressure or whatever you are controlling) range into discrete ranges is called quantisation. Quantisation leads to limitations on the controller accuracy. Possible consequences of quantisation error include loss of accuracy and random or cyclic fluctuations in the output (noise).

The PIDassist program lets you explore the effects of quantisation and sampling time. In its simulation mode you can simulate inputs and outputs with and without quantisation. It will also simulate output dithering, which is a tricky method of reducing the effects of output quantisation.

One particular aspect of quantisation that needs to be considered carefully is signals that only work across a proportion of the active range of the system's inputs and outputs. For example, if you have a temperature input scaled 0 – 200°C, and the actual temperature has a working range of only 10°C, then with an 8-bit converter (256 steps), the actual number of steps over the working temperature range is only about 13. Such a small number could well lead to problems.


PID control: Theory

In this chapter we will introduce you to the theory of closed loop control and PID control in particular. The approach is somewhat unconventional, in that the heavy mathematics is missing. Instead we will approach it from an point of view of real-work experience and descriptions that hopefully will give you some intuitive "gut feel" for the topic.

Several Excel spreadsheets are used in this work. Some contain macros. If Excel asks you if you want to enable macros please answer 'yes'.

Previous Topic

Next Topic

In This Chapter

PID theory: What is PID?

PID theory: Time constants

PID theory: Multiple time constants

PID theory: Asymmetrical time constants

PID theory: Non-linear systems

PID theory: Fixed delays

PID theory: Backlash, stickiness and other nasties

PID theory: The control model


PID theory: What is PID?

Imagine I give you an electric kettle filled with water and with a thermometer sticking out of it. The kettle is wired up via a dimmer or a Variac, so you have a knob to control the heater power. I challenge you to heat the water up to 80ºC as fast as possible but without overshooting by more than 1ºC, then maintain it at 80ºC.

PID kettle experiment

Maybe on your first attempt you decide to be a thermostat. So you turn up the knob to full power, and wait for the water to heat up. At first nothing seems to happen. Then, gradually at first, the thermometer starts to move. After 4 minutes it gets to 80ºC and you turn the knob back to zero. But lo and behold, the water continues to heat up beyond 80ºC. In fact, gets up to 87ºC before it starts to slowly cool down. As it gets down to 80ºC you turn the power on full again. But what's this, the temperature keeps dropping! Only at 76ºC does it turn around and start heading up again. After 30 minutes of this you finally conclude that the best you can achieve by being an on/off thermostat is to keep the water temperature between 76ºC and 86ºC. What you have been doing here is simple on/off control, sometimes called bang-bang control.

Let's empty out the kettle and refill it with cold water.

On the second run you decide to get smart and start turning down the power as the temperature approaches the 80ºC setpoint (setpoint is what we call the target temperature). So, you start out with full power. As the water heats up beyond 60ºC you start to slowly wind down the power. At 80ºC you have judged it perfectly so the power is zero. Guess what happens? It still overshoots! Not by as much, it peaks at only 84ºC, but it certainly overshoots. In this case you are being a simple proportional controller. You apply power to the kettle in direct proportion to how far it is away from the desired temperature. What is frustrating you is the fact that the kettle and water and thermometer, as a system, contain several delaying elements. It takes time for the heating element to heat up. It takes time for the water to heat up. It takes time for the heated water at the bottom of the kettle to raise up to the thermometer. And finally the thermometer, fast as it is, takes some finite time to heat up and register the change. What all this means is that by the time you apply a proportional correction, it's far too late. The die was cast maybe 5 or 10 seconds ago. And you can only sit there and helplessly watch the mercury heading north.

OK, back to the cold water tap (faucet if you are American)!

On the third run you decide to be very smart (not gonna let this damned thing get the better of us!) and anticipate the temperature. So, as before you start winding back the power, but you start sooner. In fact, on run 2 you got 4ºC overshoot, so if you can time it to hit zero power at 76ºC, you have a jolly good chance of it coasting nicely up to 80ºC and stopping there. And it works! Well, for a while at least. But as the temperature peaks at 80ºC it starts to drop back again and you now get caught up in trying to maintain it on station.

Let's stop playing with the kettle and think about this for a while.

In run 2 we applied a heating power proportional to the error between the setpoint (where we want to be) and the actual temperature (the Controlled Value or CV) where we actually are. This is the factor that gives PID the 'P'. This is something fairly simple to understand intuitively. We worked the knob so the power would be full up to 60ºC and then drop off smoothly to 0 over the next 20ºC. If the kettle has a 2000W heating element that means we apply power at the rate of 100W/ºC of temperature error (though it maxes out at 2000W). Please ignore for now the fact that if the water is too hot, we can't suck heat out of it (apply negative power)! Imagine now that we somehow discover that it requires 200W to hold the water at the 80ºC setpoint. To get 200W of power from the proportional correction alone, at a ratio of 100W/ºC, means we need an error of 2ºC to "drive" the system. If the ambient temperature is 20ºC, 80ºC represents 60ºC worth of heating from a 2ºC error. That's a ratio of 30:1 which is our "proportional gain", and we call it Ap (A-subscript-p).

We need a 2ºC error to "drive" the outcome?! Hmmm... better get back to that one later.

In run 3 we decided to anticipate where the temperature was heading to. Now, you may have to take this on trust: The mathematics for this is that we apply a moderating effect proportional to the rate of change of the Controlled Variable. The faster the CV is changing the more we will tend to back off on the power control (or advance the power control if the temperature is falling). In maths Rate of Change is called "derivative with respect to time" or simply "Derivative". That's where the 'D' in PID comes from. Sometimes derivative is called the "rate term". There are two parameters associated with Derivative, a time setting Td (a variant of how much action to apply for a given rate of change of the CV), and a gain term Ad, which is normally set as a constant and never changed. More about the time setting later.

That accounts for the P and the D. What about the I?

OK, remember how a couple of paragraphs ago we deduced that we needed some error in the end result so it could get multiplied by Ap and provide the steady state power to maintain the temperature? Imagine now that we have diligently applied our power proportional to error strategy, and our back-off according to rate of change, and the temperature is sitting nice and steady at 78°C (the best we can do with just proportional and derivative, at least with Ap=30°C/°C). We now look at the temperature and say "let's wind up the knob, ever so slowly, at a speed proportional to the error". So we set to, let's say every 10 seconds, to look at the temperature, calculate the error and then give the knob a minuscule nudge proportional to the error. Slowly, every so slowly, we will inch the temperature up towards 80°C, but at a rate that slows as it approaches the setpoint. The effect of all the tiny nudges of the knob is cumulative: 10mW for the first, 9.99mW for the 2nd (less because the error is a tiny bit less), 9.98mW for the 3rd, etc. But they all add up! 10, 19.99, 29.97, etc. This cumulative effect is mathematically called integration. That is where the 'I' comes from in PID. Integral is specified as an Integrator Time, referred to as Ti. Ti is the time it will take for the integral action to move the control action an amount equal to the prevailing error. For example, if the integral time is 25 seconds, and the controller error is 3%, then the output of the controller will ramp by 3% every 25 seconds.The neat thing about integration is that as long as Ti is non-zero, the integral action will keep on working until it cancels out the whole error, and we wind up dead bang on the setpoint.

Neat, heh?

Well, that all looks fine in theory. In practice we have to find suitable values for Ap, Ti and Td, and that can be difficult. With a bad set of values we will get a plain old mess that oscillates all over the place. In addition, many of the processes we may wish to control have nasties like sticky valves, backlash, noise, non-linearities, saturation and asymmetry. Many of these things are impossible to analyse and predict even if you have a Ph.D. in maths. If, like me, you are challenged by a mere set of simultaneous nonlinear differential equations, the problems may seem insurmountable. The answer lies in trying to get an intuitive feel for what is going on, and to use simulation and other tools to get a handle on the problem.

In the next section we will study the dynamics a little more closely, to get a feel for how things change with time.


PID theory: Time constants

Mastering PID control is largely about mastering the dynamics of the situation, i.e. how things change with time. In our kettle experiment we discovered that the delays in the kettle-water-thermometer system made it difficult to control the temperature.

Most theories of PID control analyse the dynamics in terms of frequency response, with poles and zeros or Bode plots. That's all very well for the mathematicians, but not very helpful if you have neither the math nor the fancy test equipment.

Our approach is different. We will use the time domain response, i.e. how the system changes with time. That is something you can be observe quite easily and understand intuitively. PID: Thermometer heatup graph

Think back to our electric kettle and the thermometer. Let's take the thermometer out of the kettle and allow it to cool down to room temperature (20°C) and meanwhile heat the water up to 80°C. When everything is stable we stick the thermometer into the water and observe the mercury. Does it go instantly to 80°C? No, it takes time. If you observe carefully you will notice that it moves quickly at first, but as it approaches 80°C it slows down. Mathematically what is happening is that the rate at which the mercury moves (heats up) is proportional to the difference between its instantaneous temperature and the temperature it is heading towards. The closer it gets to the final destination, the slower it moves, because there is less difference to "drive" the change.

The graph shows the readout versus time curve. The following table shows how you can create such a curve for yourself in Excel. The first column is just time, in 0.1 second increments. The second column is the temperature as actually read out. The 3rd column is the difference between the readout and 80°C. The final column is the difference multiplied by 0.2 (or any constant <1).

Now here's the trick: The second readout value is calculated as the sum of the readout in the row above plus the 4th column in the row above. This is repeated right down the table.

Time (s) | Readout | Difference from 80 | 0.2 * difference
0.0 | 20.0 | 60.0 | 12.0
0.1 | 32.0 | 48.0 | 9.6
0.2 | 41.6 | 38.4 | 7.7
0.3 | 49.3 | 30.7 | 6.1
0.4 | 55.4 | 24.6 | 4.9
.... Several rows omitted ....
1.3 | 76.7 | 3.3 | 0.7
1.4 | 77.4 | 2.6 | 0.5
1.5 | 77.9 | 2.1 | 0.4
1.6 | 78.3 | 1.7 | 0.3
1.7 | 78.6 | 1.4 | 0.3
1.8 | 78.9 | 1.1 | 0.22
1.9 | 79.1 | 0.9 | 0.17
2.0 | 79.3 | 0.7 | 0.14
2.1 | 79.4 | 0.6 | 0.11
2.2 | 79.6 | 0.4 | 0.09

If you have installed PIDassist to the default folder, it will be in C:\Program Files\SPLat\PIDassist. In that folder you will find a subfolder \spreadsheets. In that folder you will find a spreadsheet file named Thermometer.xls. Assuming you have Excel on your computer, you can now examine that file. If you don't have Excel, you can download a free office suite called OpenOffice from www.openoffice.org. This contains a spreadsheet program that will do some of what Excel can do.

What you will see when you launch Thermometer.xls is the original of the above table and graph. Invest a few minutes to explore how each Readout value except the first is calculated from the Readout and Difference in the row above.

Now select Sheet 2 from the tabs at the bottom of the page. What you will see here is a similar page, but with different stuff in the spreadsheet and a second curve added to the graph. In row 4 you will see an entry called "Tau", and if you investigate you will see that Tau is used in the equation for each entry in the Readout column.

Take a look at the equation for the cells in the Readout column. It is of the form:

Reading = 20+60*(1-EXP(-A10/$B$4)) where

20 is the starting temperature

60 is the total temperature change

EXP(...) is the exponential function, meaning the number e raised to the power of something. (e is a number the mathematicians like, something like Pi. Its value is 2.718282....)

A10 is the time value from the lefthand column.

$B$4 is Tau

Let's re-write this into a more "mathematical" form:

Reading(function of t) = Initial_Value + Total_Excursion * (1 - Exp(-t / Tau))

If the Exp (exponential) function seems scary, don't worry. You don't need to understand how it works, but it helps to understand something of how it behaves. It may interest you to know this is the same exponential function that governs compound interest and bacteria growth. But because of the minus sign in front of 't', instead of growing without bound it grows towards a limit it can never quite reach.

OK, back to Sheet 2!

The initial value of Tau is set to 0.6. See if you can find a value of Tau that makes the pink curve exactly overlap the blue curve (which is the one from Sheet 1). Hint: Change Tau by about 0.1 at a time, and observe the effect.

Now look at the other columns in Sheet 2. "°C Change" is simply the Readout column with the 20°C starting value subtracted. "% Change" is the change as a percentage of the total 60°C change. This last column is the most useful one for understanding the curves in a generalised sense, because the numbers are normalised back to percentage change. In that column it won't matter if we are talking °C, rpm, PSI or whatever.

Now go back again to Tau, and set it to 1. Look at the row for t=1 (row 17) and read off the % Change value. It is 63.2%. Alter Tau to 2 and read off the % Change value for t=2 (row 27). It is still 63.2%.

The number Tau is what we call the time constant. When t=Tau, the exponential function becomes Exp(-1)=0.367879 and the term (1-Exp(-1)) becomes 0.632..., which is where the 63.2% number comes from.

Very many systems have a dynamic behaviour that follows the exponential law. If we know the time constant of such a system, we have a good handle on its dynamic behaviour. The time constant can be measured by applying an "input" (voltage, heat, power, pressure) to the system and determining when in time it reaches 62.3% of its final excursion.

As an exercise, fiddle with Tau on the spreadsheet and determine how far the output (% Change) gets in 2 time constants, 3, 4 etc. Also, see if you can find the relationship between Tau and the 0.2 multiplier we used in Sheet 1.

As a matter of interest, Sheet 2 represents the "continuous process" as it takes place in the real world, whereas Sheet 1 represents the sampled system approach that forms the basis of a digital implementation of a PID control loop.

If you are into electronics you will recognize this exponential time constant stuff as being equivalent to a simple Resistor-Capacitor low pass filter with a time constant of R*C.

In the next section we will look at systems that have multiple time constants.


PID theory: Multiple time constants

In the previous section we looked at a simple dynamic system, a thermometer, and developed the concept of a the time constant, being how long it takes for 62.3% of any change to appear in the output. What about a more complex system, like our initial heater, water and thermometer?

It turns out that systems may have, and very often do have, multiple time constants that are cascaded. That means the output of one time constant element drives the input of another. In the electric kettle experiment there are probably at least 3: The heating element, the water and the thermometer.

Let's see what we can expect out of such a system if we apply a step change to its input (by step change we mean we apply a certain level of power (or whatever) and hold it constant) while we observe the output. PID multiple time constant graphs

The spreadsheet "multipleTC.xls" lets you explore multiple time constants. (This spreadsheet contains macro code, but it is quite harmless ... trust me!)

multipleTC.xls has three cascaded time constants, represented by the columns Stage 1, Stage 2 and Stage 3. The "input" to each column is the column to its immediate left. Hence, the input to Stage 1 is a column full of 1's. We call this a "unity step input". You can toggle the input to become 1's for half the time and 0's for the rest, using the "Input" button. More on that later ... (if you are using OpenOffice, the Input button won't work. Move it aside and instead alternate the contents of cell H3 between 1 and 0. Beware: OpenOffice is very slow, so you may not see a result for a few seconds. Also, the curves will be in different colours that do not key to the column headings.)

Above each column is a constant 'K'. This controls the effective time constant for that column. The higher K, the shorter the time constant. If you set K=1 you effectively bypass that stage. If you set K=0 you create an infinite time constant, and the output will never change.

The formula for each new value is

New_Value = Old_Value * (1-K) + Input * K

Basically this equation says the following: Suppose K is 0.1. Then for each new input reading, we produce a new output that consists of 10% of (K times) the new input plus 90% of (1-K times) the old output. That means that if the input changes we don't pass the whole change to the output right away. The smaller K is the longer it will take the output to catch up with the input.

The 3 stages are plotted in blue for the 1st stage output, magenta for the 2nd stage output and yellow for the 3rd stage output. The input is plotted in turquoise. You can change the input with the "Input" button.

The initial K values are 0.005, 0.05 and 0.05. If you look down the Stage 1 column for a value of 0.632 (the "magical" 1 time-constant value) you will find the closest value at row 200. It just happens that 200 is 1/K for that column.

For time constants much greater than the sampling interval, K is approximately Tau/Ts (Ts being the sampling time). A better formula is

K = 1 - Exp( -Ts / Tau)

This gives the right answers for all time constants. Remember however that a sampled system is an approximate representation of a continuous process, and it will get worse the longer Ts is relative to Tau.

Take a look now at the very start of the graph. Make sure the K values are set back to their original settings if you have been playing with them. The yellow curve (Stage 3) seems to start slowly and then accelerate. There's an initial hesitation before it gets going. That was exactly what happened with the electric kettle when we first applied the power! In fact, at first we couldn't see any change at all.

Imagine what the means for our poor controller. It applies a control input to the process, and at first nothing seems to happen at all. Without going into too much theory, the more cascaded time constants a process contains, the harder it will be to a control. What's more the closer together the slowest of those time constants are, the worse it will be. We call the slowest time constant the "dominant time constant". If the dominant time constant is say 10 minutes and the next slowest one is 1 second, we can probably ignore all but the dominant one. If the next slowest time constant is 9 minutes, we are more likely to have trouble.

Let's now look at what happens when the input turns off. Click the "Input" button. This makes the input turn off at the half-way point across the graph. You should immediately notice that the yellow output continues to rise after the input goes away. There's the cause of the overshoot we had with the kettle! (Remember in the first run with the kettle, we turned off the power at 80ºC but the temperature coasted up to 87ºC?). Try changing the K factors for stages 2 and 3. With values closer to K for stage 1 the relative overshoot can become quite severe.


PID theory: Asymmetrical time constants

There are systems that clearly move more quickly in one direction than the other. However, you need to be careful with this one, in many cases the cause of this asymmetrical behaviour is not what it seems to be. As an example, consider our kettle. With the heat on, the temperature increased quickly, but with it off it dropped a lot slower. Your first reaction might be to say: "well, there are obviously two time constants at work here, one for heating and the other for cooling". NOT SO. Neglecting various secondary effects, there is one time constant at work all the time. The different rates that we observe are due to the different "forcing functions" that are in play. During heating we have an electric element producing maybe 500 watts, and during cooling we have a loss of heat to the environment, maybe 30 watts. That's what causes the heating to be fast, and the cooling slow. If you arranged for the cooling heat removal to be 500 watts, for example by putting the poor kettle into iced water (or liquid nitrogen if necessary), you'd find, of course, that the temperature dropped just as fast as it went up.

This distinction is not just semantics. If you assumed a system model for your kettle with 2 time constants based on the measured temperature change rates, and did a continuous PID control design on that basis, you could well end up with a control system that failed to work. So you can see how it can be tricky to define the characteristics of real world systems.

Genuine 2-time constant - one when ascending, one when descending – systems do exist. Their essential characteristic is difference in response times for up and down directions for forcing functions of opposite sign but SAME MAGNITUDE. If you suspect this behaviour, you should look for an explicit mechanism – such as check valves, diodes or similar devices with asymmetrical behavior. If you can't see any mechanism of this sort, look very carefully at the situation and see if you're being tricked as with the kettle example. Control design can be an intriguing business!

The asymmetrical time constant capability within PIDAssist allows that configuration to be modelled if it is encountered.

The spreadsheet "assymetricTC.xls" lets you explore asymmetrical time constants. It has the same 3 columns as multipleTC.xls, but each column has two K factors, one for going up and one for going down. The initial values are:

Kup | 0.02 | 0.1 | 0.1
Kdown | 0.01 | 0.01 | 0.02

The input is set initially to alternate between 1 and 0 and back again every 200 steps. The resultant outputs look like the adjacent graph PID assymetric time constant graph

The PIDassist program allows you to simulate up to 3 cascaded asymmetrical time constants.

In electronic terms an asymmetrical time constant could be approximated with a capacitor, two series resistors and a diode connected across one of the resistors.


PID theory: Non-linear systems

More than any other section of this tutorial, you should treat this one as broad background rather than as instructions to be specifically applied. Even highly experienced control engineers tread VERY carefully when the word 'nonlinear' pops up. Amazingly, even with today's technology, there are no formal ways for dealing with nonlinear systems, and adding nonlinearity to a control system can cause very unpredictable behaviour. Basically, your approach should be to identify possible nonlinearities within your application – if it looks like there aren't any (careful, they're very good at hiding), breathe a sigh of relief and proceed. If there are, proceed anyway but consider getting some assistance if your application is critical.

A non-linear system, in the context of closed loop control, is a system that contains elements whose output versus input graph is not a straight line. We are not talking here about the output versus time, but rather the final steady state output for a given input.

Consider our electric kettle. Suppose we connect it into a control system with a temperature sensor and an electronically controlled "dimmer" that produces an output voltage proportional to its input control voltage. Suppose the temperature sensor is linear (its output voltage is directly proportional to temperature). So we have a linear sensor and a linear element voltage versus control. Is the overall system therefore linear?

No, it's not. For a start, the heat generated by the element is proportional to power, not voltage. And for a resistive element power is voltage squared divided by resistance. So right there we have a square law not a linear law.

Secondly, remember we determined that our kettle needed 200W to hold the water temperature at 80Cº, (60ºC above an ambient of 20ºC). The kettle had a 2000W element. Hence, if we turn on full power the water will heat up by 10 times as much, i.e. 600ºC.

Hang on, hang on. Heat water up by 600ºC?

Water boils at 100ºC, so what happens if we pour 2000W continuously into the kettle? The water will boil, and at 100ºC it will get no hotter. Instead, the energy will all go into turning the water into steam, until it has all boiled away and we have a melted down kettle to explain to our spouse. Clearly, the temperature versus power graph flattens out at 100ºC, so temperature ceases to be proportional to power or to the square of voltage. PID: Kettle transfer function

The graph shows the temperature in green, plotted against voltage expressed as a percentage of full voltage (120V or 240V, take your pick). The magenta plot is the "gain" of the kettle. Gain is the slope of the temperature versus voltage graph, and indicates the sensitivity to a voltage change. Gain varies with voltage. Below about 33% gain increases from zero to some maximum. It then drops sharply to 0.

The flattening of the green curve above 33% of voltage is called saturation or limiting (or boiling or melt-down in the case of our kettle).

All real systems saturate at some point. Nasty things can happen in a control system if you hit saturation. That means you need to carefully consider how and when your system may saturate, and make sure it will not go berserk if saturation is unavoidable at some time. If saturation is possible you will need to devise tests to force it into saturation to check its behaviour is acceptable.

Getting back to the non-linear gain (i.e. gain that varies across the operating range). Later you will learn that gain is one of the important parameters in a PID controller. The more gain, the more the system (controller + process) will tend to become unstable and oscillate. The less gain the less accurate the system will tend to be. You may therefore need to plot the gain of your process and then simulate and test at several points using the gain figures. You will also have to evaluate possible changes in gain due to external influences like temperature, supply voltage, friction or whatever.

The PIDassist program allows you some ability to simulate saturation but not other non-linearities. It also lets you set the gain of the simulated process.


PID theory: Fixed delays

In previous sections we have explored the delay mechanism generated by "time constants". You may recall that with at least a simple time constant, the moment the input is applied the outputs starts to change. With multiple time constants the outputs seems to hesitate, but in fact it does still start to change immediately, albeit very slowly.

What about true delays? This is the sort of delay you would get if you had a water heater with a long outlet pipe and a temperature sensor at the end of the pipe. Chances are you experience exactly that kind of delay each time you take a shower. For a start, the water must travel from the heater to the shower head. And even after that delay, once the hot water has come through there is a perceptible delay between adjusting one of the taps and the warmer or cooler water getting to the shower head.

Fixed delays like this are called Dead Time or Transport Delay (take your pick). Sometimes the term Dead Time is incorrectly used to describe the apparent initial delay we see when there are multiple time constants. Transport delays can add further to the challenge of getting a PID control system tuned up. They are not usually impossible to handle, especially if significantly less than the dominant time constant, but they can be a challenge. The main thing is that you are aware of them, and also that some types of "fixed" delay can actually vary. For example, the more water flow you have in your shower the shorter the delay (can you figure out why?)

The PIDassist program allows you to simulate a fixed delay. When you simulate your system, you should always test it over the expected range of delays, as response of PID controllers is strongly influenced by delay. More on this later.


PID theory: Backlash, stickiness and other nasties

Especially in mechanical processes there are varying mechanical imperfections that can impair the function of a control system. These include things like:

Backlash, sometimes also called hysteresis. This is what you get when a normal gear or chain drive reverses. There is a small amount of slack in the coupling, so the shaft has to travel back a small amount before it has taken up the slack. Backlash will limit the attainable accuracy of the system and very likely also lead to small oscillations around the setpoint that can't be eliminated by PID adjustments. By far the recommended approach to dealing with backlash, as well as all other mechanical imperfections, is to eliminate it as far as possible. For example, low backlash gears can be used. Another trick that is used sometimes is to deliberately create a deadband in the controller. A deadband is created by making the controller ignore process error less than a certain amount. However, this can create problems of its own with certain types of system.

Stiction (sticky friction). This is the effect whereby it takes more force to start two surfaces sliding against each other than to maintain the movement. It can produce a jerky output. It is most commonly encountered in proportional control valves. Search online for stiction. Just about any sort of closed loop control will suffer if it has to deal with a significant amount of stiction, reducing the effect is the recommended approach.

One thing to be cautious about with systems that display these mechanical imperfections is that they can make unstable systems look stable when things are steady. Everything may work fine until a big disturbance comes along, and all of a sudden your device is swinging wildly from one extremity to the other, and the only way to stop it is to turn it off. You would definitely not want to sell a large number of items with this characteristic!


PID theory: The control model

The actual PID controller is implemented in a SPLat (or any other digital controller) as a bunch of numerical calculations. There are several ways of performing the necessary calculations, depending on the exact "circuit configuration" your program is emulating. The program we use implements the configuration in the diagram.

PID controller block diagram

In this diagram the circles represents summations or subtractions. Hence the left-hand circle calculates the setpoint minus the process feedback minus the derivative to produce an error signal. Arguably the derivative should be subtracted in the righthand "summer", but this way produces slightly simpler code and works just as well. The error is multiplied by Ap (proportional gain) in the green box and integrated in the yellow box, then the two are added together to produce the final controller output.

In the actual programs the derivative is calculated in slightly round-about way. Instead of calculating the difference between successive input samples (which would be the "natural" way of doing it), we put the input through one of the time constant calculations we have been studying earlier, to calculate a moving average, then subtract that from the direct input to get the derivative. For this reason the differentiator has an associated time constant Td ('Derivative time') as well as a gain setting Ad.

The integrator is implemented as an ongoing adding up of the error at each sample, times a scaling factor. The factor specifying integral is 'Integral time' – Ti - which is the time it takes for the output to ramp an amount equal to the prevailing controller error.

The error amplifier is just a multiplication by Ap.

The calculation, performed each sampling interval, goes like this:

  • Let Kd = the factor required to get a time constant of Td
  • Call the process feedback "Input"

Then:

  1. New_LPFin = Old_LPFin * (1-Kd) + Input * Kd (this is the moving average of the input, which in electronics parlance is the input lowpass filtered.
  2. Derivative = Input - New_LPFin
  3. Old_LPFin = New_LPFin (for next time)
  4. Error = SetPoint - Input - Derivative * Ad
  5. Integral = Integral + Error
  6. Output = Error * Ap + Integral

In practice, there is one other wrinkle. In our code we limit the range of integral values to the valid range of output values (the integrator is designed to "saturate"). This combats "integrator windup", where the integrator can build up excessive values if the process is slow to respond due to saturation elsewhere.


PID control: Using PIDassist

PID assistant full screen 1

In this chapter we will get you familiar with the PIDassist program. PIDassist was written to help you with several of the steps required to implement a PID control. It lets you simulate a range of processes and the interaction with a controller. The controller section uses identical math to the sample SPLat programs provided.

You should now install PIDassist and launch it. If you have a SPLat controller available connect it to the computer. It will act as a "dongle" to unlock some of the features of PIDassist.

The next several sections describe the various parts of PIDassist. In the next chapter we will outline the steps to take to investigate your process, establish a set of PID control parameters and implement a control program in SPLat.

Everything in PIDassist is done with levels (voltages or whatever you want to call them) normalised to the range 0 to 1. The full input range of the controller is 0 to 1, and its output range is 0 to 1.

PIDassist is a work in progress, so please be understanding if the screen shots vary somewhat from the program version you have.

Previous Topic

Next Topic

In This Chapter

PIDassist: The process simulator

PIDassist: The controller

PIDassist: Display

PIDassist: Modes

PIDassist: Status bar

PIDassist: Code generator

PIDassist: Saving and restoring configurations


PIDassist: The process simulator

PIDassist process pane

The process simulator will let you simulate a range of real-world processes, but not all. With sufficiently strong motivation (like a hint of an order for 10,000 SPLat boards) I can add more features.

You can have up to 3 cascaded time constants with different time constants for increasing (TauUp) and decreasing (Tdown) outputs. Stage 1 receives the output of the controller, then feeds into stage 2 which in turn feeds stage 3. You can bypass a stage by unchecking its Enable checkbox. The output of each stage can be limited between MinOut and MaxOut. As the stage gain is always 1 and the input to the chain (output of the controller) is limited to the range 0-1, it would make sense only to constrain the stage outputs to values within the same range, for example 0.2 to 0.76.

The output control lets you set the final gain of the process and also apply limits. It can be useful to set the final gain to slightly more or less than 1, so the red and green display graphs don't overlap (gain change of 5-10% should have negligible effect on the PID control). The Offset adds a fixed amount to the output. This is useful for example where you are temperature controlling something, and you need to add ambient temperature to the "reading". You can also use it to inject a disturbance into the process.

If your real process is inverting, i.e. if its output decreases for an increasing control input, you need to use a negative gain value and a positive output offset.

There is also a delay line. This is quite simply implemented as an array of numbers that are shifted bucket brigade style once per sample interval. You should use the delay line only if you are quite sure your process contains a true delay such as a pipe, a conveyor or an auger. The official term for such a delay is Dead Time.

The simulator is re-calculated at the same sampling interval as the controller. The K factors used are automatically adjusted to get the required time constant. However, any sampled system is only an approximation to a real-world continuous system, so try and keep the sampling time at least ten times faster than the shortest time constant in the simulator, to minimize errors.


PIDassist: The controller

PIDassist controller control pane.gif

The controller function in PIDassist closely reflects the sample SPLat code we have provide you with. Several of the parameters in this pane are also discussed under PID theory: The control model

Input pane.

Ts. This is the sampling time, how often the program will take a reading from the process output and do a set of PID calculations. You should set this to some multiple of 0.01s (10mS), because that is the basic timing capability of a SPLat. The PID calculations take about 3.5mS in SPLat, so with a 10mS sampling time you will use up 35% of the boards calculating capacity (these numbers are for generation boards like MMi200 and SL99. The 1st generation OEM36 is about 8 times slower).

PIDassist uses Ts to calculate constants to use for many other parameters. That means you can change Ts without needing to re-enter other parameters. If you see any change in the results when you change Ts, it is because of errors result from the sampling process itself.

8 bit quant When you have this checked. PIDassist will simulate the effect of 8-bit input resolution (quantisation). The input number range is 0 to 1. With input quantisation turned on the input number is coerced to the nearest multiple of 1/256. The accuracy of the controller is limited to this resolution.

Inverting. When you have this checked the input is inverted. That means instead of an increasing input being interpreted as the process output increasing, it is taken to mean the process output is decreasing. What actually happens is that the input from the process is subtracted for 1.

Note: Your process is "inverting" if its outputs will decrease for an increasing control input. If that is the case you should use inverting in the controller simulator. That will ensure that when you come to run PID code in a SPLat board it will operate correctly (our boiler plate programs and the code generator in PIDassist know about inversion). However, you must manually set up the process simulator to invert be using a negative gain value and an output offset (typically of 1).

Derivative pane

Derivative time Td. This sets the amount of derivative used. The higher this value the greater the amount of derivative is applied. Specifically, higher derivative time increases the extent to which slower moving signals are included in derivative effect.

The Derivative Gain factor Ad is internally set to a value of 4.

Integral pane

Integral time Ti. This is the integral time. The LOWER the value, the greater the amount of integral applied. If this is set to, for example, 10 seconds, then with a steady 5% error between control variable and setpoint, the controller output will ramp at 5% every 10 seconds – and keep on ramping all the way to its maximum or minimum limit as long as the error remains.

Prop pane

Ap. This is the proportional gain. Higher values mean more proportional action.

Output pane

Quantisation = Infinite. If you select this PIDassist will not apply any output quantisation. This is not something that can be achieved in the real world with a digital controller. However, it is useful to use infinite resolution during tuning, so you can get

Quantisation = 8 bit . Enabling this makes the PIDassist program emulate the effect of 8 bit output resolution (quantisation) in a SPLat board, which would be typically the case if you are using an analog output. The output number range for the simulator is 0 to 1. With output quantisation turned on the output number is coerced to the nearest multiple of 1/256. The effect of output quantisation is usually that the output will "hunt" between two values so as to average out at the value needed to keep the process at the setpoint. This in turn may lead to a small amount of jitter in the process output.

Quantisation = 1 bit. A 1-bit output means the output switches on or off, but cannot be anywhere in between. This would be the case with for instance a heater driven off a relay.

PWM filter. In second generation SPLat boards like MMi200 and SL99, the analog output is implemented using a pulse width modulator (PWM) and a low pass filter. The PWM produces a 0V or 5V on/off rectangular wave that has a duty cycle (ratio between on time and total period) proportional to the required analog output. This is passed through a simple low pass filter R-C with a time constant of 100mS. PWM filter enables emulation of this filter. You only need to bother with this is you are using a sampling time less than 100mS and the process has time constants down in that range.

Dither (bits). Dithering is a technique of deliberately adding a small amount of noise to a quantised signal in order to give an apparent improvement in resolution. In PIDassist and the sample SPLat programs, dithering is added by a cyclic counter of 1, 2, 4 or 5 bits. This counter is incremented once per sample interval, scaled back to a total range of one LSB, then added to the output just before quantisation. If the sampling time is considerably shorter than the dominant time constant in the process, this can be quite effective. The dithering will add periodic noise to the controller output, but if the process is much slower it won't show up in the output (naturally it's up to you to be sure there is not bad effect further upstream, like wearing out a control valve due to excessive to and fro).

Number. The number in this pane is the controller output.

Note: In open loop mode the output quantisation is inactive.

Setpoint pane

SP. This is the actual setpoint, the magic number we want out of our process.

Other controls. The two other windows, and the button in between, let you set up two alternative setpoints and toggle between them. The one on the left should be the lower of the two or it may not work. The cycle check box turns on automatic cycling between the two setpoints every 5 divisions of the display. Changing the setpoint and observing the behaviour of the control system is a common way of testing. This automatic setpoint toggling works even when the control is taking place in a SPLat board hooked up to a real process. What with this and data logging you can have automatic unattended testing for slow processes ... providing you take appropriate safety precautions!

Open Loop checkbox

The Open Loop checkbox switches the simulation to open loop mode. In open loop the setpoint is sent directly to the process. This is done to allow you to investigate the step response of the simulated process.


PIDassist: Display

PIDassist display and control pane

PIDassist features an oscilloscope style display and a basic data logging facility.

The green trace is the output of the process. The red trace is the output of the controller. There are 20 horizontal divisions. At the fastest Horizontal rate there are 500 samples per screen width, more at slower rates.

The Pause button stops the simulation and pauses the display. It is shown above in the "unpaused" state, meaning the simulation is running.

Horiz. S/div sets the horizontal time base. Note that the available settings are multiples of the sampling interval (which is set in the controller pane). The scale will change when you change the sampling interval. During simulation the scale is Seconds/division. During operation in a SPLat board the scale is Samples/division (in that mode the SPLat board sets the pace).

Vert scale sets the vertical scale factor for both traces. It can go down to 1 as show and up to silly numbers. You would normally use it on 1, or maybe on 2 if the process output is peaking at more than 1 (because nearly everything is in the normalised range 0 to 1).

Anti-aliasing can sometimes get rid of artificially produced low frequency signals (the mechanism is the same one that makes the wagon wheels go backwards on old cowboy movies). We use a min/max algorithm. If you find a periodic wave appears as you slow down the time base, it may be aliasing. Turn on anti-aliasing and see if it goes away.

Log to file turns on logging. All data points (every sample) are written to a comma delimited (*.csv) file that can be read by Excel. The default file name is default.csv. You can change the file name via the File menu. The green "LED" flashes each time a sample is logged. Note that every sample is logged, so you could wind up with a very large file! The file format consists of two columns of data. The first one is the process output and the second one is the controller output.

Clr log simply clears the log file. There are no annoying "Are you sure?" boxes, so beware.

To the right of the Vert scale control are two numbers. The top one is the raw process output. The bottom one is the same data fed through an adaptive low pass filter so you can better see the average of a value that is jittering or oscillating slightly. It pays to look at these numbers from time to time, because a simulation can look very stable but have settled at an incorrect value due to inappropriate settings.


PIDassist: Modes

PID modes pane

PIDassist can be operated in 3 main modes:

All in PC. The controller is simulated in the PC as well as the target (controlled) process. The SPLat board serves only as a dongle to unlock certain features.

SPLat + PC. The PID control calculations are performed in the SPLat board, by a program we supply, with the process simulated in the PC. Communication between the two is via SPLatLink. This is very slow, because the serial data communications take so much time. I used this early in the development of PIDassist and will eventually remove it.

All in SPLat. In this mode the SPLat is allowed to control the real process. The PC and PIDassist can be used to fine-tune the PID control parameters within the SPLat board. To do this you have to be using a SPLat program we supply that knows how to use PC-supplied parameters. In this mode two bytes of data are extracted from the SPLat board and displayed/logged. This opens the door to logging the performance of your PID system. It also to gives you a way of characterizing your process at an earlier stage of the proceedings.


PIDassist: Status bar

PIDassist status bar

The bottom of the PIDassist screen is the status bar. This has several panels. From left to right these are:

  1. An expanded explanation of the Mode.
  2. An undo counter. This shows you how many undo levels have been captured.
  3. The current log file name
  4. The SPLat (board) connection.

The last one is the most important one, and also doubles as a connect/disconnect button. PIDassist will automatically scan all COM ports looking for a SPLat board. While that is taking place the connection status panel will show what's happening. If you click on the panel it will "isolate" the board, meaning it disconnects from the board (if connected) and stops scanning. This is useful to stop "jerky" operation of PIDassist when there's no board present, and also to release the COM port if you want to talk to the board with SPLat/PC (only one application program at a time can use a COM port).


PIDassist: Code generator

PIDassist code generator pane

This is the last feature you will use, but you will like it! This pane will generate the PID controller code for you. All you have to do is copy and paste it into your SPLat program and add a GoSub to it.

Well, it might take a bit more than that, but not much. It will depend on whether you have any need to customize the code. Because you are implementing PID in SPLat as some exposed code, it means you have the option to add special features to optimize it for your work. Examples would be:

  • Presetting the integrator and the output to non-zero values at startup.
  • Freezing or remembering the integral and the output while something disruptive is happening to the process (maybe a purge cycle).
  • Dynamically changing the PID parameters.
  • Adding sensor linearisation
  • Adding a custom output drive.

The code generator allows you to generate a program with built in timing for the cycle time or which will assume the timing is done elsewhere. It also lets you determine where in RAM (Data Memory) the PID function's storage will be. There are many other variables that go into generating the code. These are taken from the PIDassist Controller pane and include the PID parameters and output dithering. You can also generate code where the setpoint is a fixed value.


PIDassist: Saving and restoring configurations

You can save your settings to a file. We call this a configuration file. Configuration files have the file name extension .pid.

There is a default configuration file called, believe it or not, Default.pid. This is the configuration loaded at startup.

Use the File menu to open and save configuration files.

As you fiddle around with settings you may wish to return to a previous setting. The Edit menu provides you with Undo and Redo functions. The undo (CTRL+Z) takes you back to where you were the last time you "unpressed" the Pause button, i.e. resumed simulation. Because clicking many of the configuration items automatically Pauses simulation, this means one full "session" of changes is captured at once. Changes that do not result in pausing simulation are captured on the fly. The status bar at the bottom of the PIDassist screen contains an indicator of how deep the Undo's are nested. Redo (CTRL+Y) winds the "tape" forward. The undo is reset when you load a configuration file.

Experiment!


PID control: Implementation

We will now get down to the actual details of implementing a PID controller for your process.

In this chapter I will take you through a step by step procedure. The underlying principle of this procedure is as follows:

  1. You first characterize your process, i.e. measure, study and quantify the dynamics of the process, be it pH control in fish tank (warning, could be very nonlinear) or temperature control in an autoclave. You do this using a SPLat board, interfaced to your system, as a data logger. You use Excel spreadsheets to analyse the readings.
  2. You then model (simulate) the process using PIDassist, using the parameters determined in step 1. The reason for modelling that it is usually easier, quicker and safer to experiment on a computer than in a real life situation. You will use the model, along with a simulation of the SPLat PID program, to establish values for the various PID controller parameters.
  3. You commit to a real program running live on a real system, but still using PIDassist to fine tune parameters and log results.
  4. You generate "production" PID code using the built in code generator in PIDassist and integrate it into the rest of your SPLat program.
  5. Finally, you recheck the tuning of your control system on the real system using the SPLat controller, including comprehensive testing across the full range of operation of the device. You should NEVER, EVER omit this step or deal with it cursorily.

A couple of these steps are split into 2 separate sections.

Previous Topic

Next Topic

In This Chapter

PID implementation: characterizing your process

PID implementation: Modeling your process (open loop)

PID implementation: Tuning

PID implementation: Interactive control


PID implementation: characterizing your process

The objective of "characterizing" the process is to determine its dynamic behaviour, which to us means its time constants, gain and possibly fixed delay (dead time). The way we do that is to apply a "step input" and record the output. We then use Excel to experimentally find a set of cascaded time constants that will fit the output versus time graph. We then repeat the process for a negative step.

As a warmup exercise I suggest you set up PIDassist with the following settings:

  1. Start with whatever settings it comes up with when you launch it.
  2. In the simulator set Delay Line to 0
  3. In the simulator set Gain to 1 and all min/max values to 0/1 respectively.
  4. In the controller select (check) Open Loop
  5. In the controller set the two alternate setpoints to 0.2 and 0.8 and enable (check) Cycle.
  6. In the controller set the sample time Ts to about 1/5th of the shortest time constant in the process.

Get the simulation running, and adjust the horizontal scale until you can see the green output catch up with the red input on each section of the graph.

What you are seeing (in green) is the open loop step response of the process for increasing and decreasing outputs. It is the exact same stuff as we generated using spreadsheets in the theory chapter, and it is what you are now going to do with your process.

You can get a fair bit of help with this from PIDassist and a SPLat controller. The SPLat can drive your process and PIDassist can log the results to disk.

Previous Topic

Next Topic

In This Section

PID characterization: Preparing your process.

PID characterization: Interfacing to the SPLat

PID characterization: Gathering open loop data

PID characterization: Extracting the time constants

PID characterization: Scaling your process


PID characterization: Preparing your process.

In order to properly characterize your process you need to have it set up in a manner that allows you to run it open loop and get stable results out of it. I can explain this best by way of an actual example.

In order to prepare this course I needed a sample system to work on. I didn't really want to be messing around with electric kettles, water and 240V. I also needed something portable, so I could take it home at the weekend and back to work on Monday. So we made up a "contraption". It consists of a length of aluminium angle about 150mm long, with a 5W resistor fastened to it one end and and a thermistor the other end. The resistor is 56 Ohms, so with 24V across it it will generate 10W of heat (that's OK, because it's heatsinked (heatsunk?) to the angle. At 24V the current is about 400mA, so an MMi200 can drive it directly. On the other end of the angle we attached the thermistor. I did it like that to introduce a nice juicy delay from one end to the other (not that I expect this to be a true fixed delay (dead time), but it will certainly ensure multiple time constants). The contraption is pictured here. You can see the heater/resistor on the left and the thermistor to the right. The object at the bottom of the picture, included to provide a scale, is called a slide rule and performs computations without batteries or software.

PID test setup, bare contraption

The thermistor is fed into the MMi200 in thermistor mode. We have thermistor temperature measurement down to a fine art, so that makes it easy.

As it happened I forgot to take home the intended 24V power supply on the Friday, so I had to make to with a 12V unregulated plugpack ("wall cube") I had at home. That meant significantly less temperature rise (25% as much power). What I did was to place it inside a cardboard mailing tube and fill it up with polystyrene beanbag beads. For my purposes it didn't matter that this gave me a different system. You can't arbitrarily change your target system like that. You have to work with the actual system, and the right system, which will presumably be a prototype of some production item you intend to control with a SPLat.

Some of this is good and some is bad. The good thing was that by insulating my contraption, I was also insulating it to a large degree from environmental influences like drafts and from inadvertently being moved around on a cooler desk. Having a test setup that is stable and free of uncontrolled influences is important. The bad thing is that my 12VDC plug pack is unregulated, so right there I have an uncontrolled environmental influence. As it turned out the main run took place overnight, so the mains voltage would have been stable. The picture shows the complete unit in its red and white postal tube on my desk, and an MMi200 running the interactive PID program. PID test

Only you can know your target system or process. Think about your experimental setup carefully, and arrange it to be free of external, uncontrolled and possible unmeasurable influences. At this stage you are interested in the behaviour of your system under ideal external conditions. You are going to be testing it open loop, remember, not under closed loop control. Also, make sure that the setup you work with is truly representative of the real end-product. Make sure that every part of the equipment that may affect its behaviour is present. Don't leave out the thermal insulation or the checkvalve, and don't make do with that sample pump you don't intend to use in your final product ... use the real thing!

The corollary to all this is that once you have characterized your system and got the PID control working, any change in the process may upset it. So watch out for inadvertent or deliberate changes. Don't let Manufacturing substitute cheaper insulation or a smaller pump without your knowledge. Consider scenarios that may lead to changes in the field, such as wear and contamination. How well your controller copes with such changes is called its "robustness".


PID characterization: Interfacing to the SPLat

You will need to interface your process to the SPLat. That means driving the process's control input from an output of the SPLat, and getting feedback from the process input back into the SPLat.

The SPLat output you use may or may not be an analog output. With my contraption I used a digital output. I programmed the SPLat to vary the average power by duty cycle switching the power to the resistor, just like the "simmerstats" used on some electric stoves. I used some code that would turn the output on for 'n' out of every 256 10mS intervals, giving a cycle time of 2.56 seconds (way short enough for the contraption's dominant time constant of around 10 minutes).

I have provided you a choice of output resolutions: Infinite, 8-bit and 1-bit. 1-bit, seemingly an impossibly low resolution, is actually one of the most common and effective control modes, applied in refrigeration and a host of other applications. It is commonly called 'bang-bang' control (bang, it goes full on, bang, it goes full off!). 1-bit resolution can work quite well on things like heaters, where the target process has a long dominant time constant.

Adding noise or a periodic waveform – sometimes referred to as 'dither' - to the continuous controller output before it's chopped back to 1 bit by a comparator can create a degree of continuous control action referred to as 'duty cycle control'. However this technique should only be used if the output device can be switched on and off very frequently without adverse impact – you definitely wouldn't use it on a compressor motor. Natural measurement noise alone may sometimes be enough to provide a degree of duty cycle control. The control processor has to operate at a reasonably high sampling rate for this control mode to function well. If it is working well it can combine the benefits of good steadiness of the controlled variable close to setpoint, and rapid application of full control effort if the error increases.

Duty cycle control can be simulated with PIDAssist.

Of course, with bang-bang control there's always a small amount of cycling in the controlled variable as the controller switches the forcing function on and off. However, as long as the process has a reasonably long time constant, the residual variation can be quite acceptable. Of more concern sometimes are the full range swings in the forcing function, eg. surges in an electrical supply as a motor is turned on and off. Avoiding these step disturbances can be one advantage in going to fully continuous control (such as the growing use of variable speed drives for air conditioning compressors).

The SPLat input will probably be an analog input. I used a thermistor to sense temperature. What you use is up to you and your process. It may be a pressure sensor or a flow meter. In the latter instance it may be a pulse type, so you would use a counting digital input.

There are three major tasks in interfacing your process to the SPLat board for open loop characterization:

  1. Making the electrical interface.
  2. Making the programming (SPLatWare) interface.
  3. Get the logging working

Making the electrical interface

For the electrical interface you need an output from the SPLat that can control the process, and an output from the process back into a SPLat input. With my contraption the output was a simple digital output driving the heater/resistor, and the input was an analog input connected to a thermistor. You may have some other arrangement. If you have a custom SPLat you may have a customised interface, say a high current PWM output for driving a proportional hydraulic valve.

Making the SPLatWare interface

There is a SPLat program called OPENLOOP.SPT in the folder "/SPLat Programs" under PIDassist. That program is a template for you to use to create a simple open loop control and data logging function. Now is the time to look at OPENLOOP.SPT.

The program contains 4 places where you must insert code and one place where it is optional.

OPENLOOP.SPT interfaces to PIDassist using the SPLatLink protocol. With SPLatLink certain RAM locations within the SPLat are accessed by PIDassist. The addresses of those locations are "hardwired" in PID assist, so don't try changing them in the SPLat program.

OPENLOOP.SPT is a template for a scheme whereby the process drive is set from the MMi200 front panel. You can also set it from the SetPoint control in PIDassist. If you chose to do that, the setpoint value will appear as a floating point number at RAM address 40 (SP).

In data logging mode PIDassist monitors SPLat RAM location 49, which is called CalcFlag. Each time it changes PIDassist reads out the number in location 50, PIDin, and saves it to the log file. (it also logs address 51, PIDout, but it is not used at this stage). Because it is changes in CalcFlag that determine the logging frequency, the SPLat program is in control of timing. If you study OPENLOOP.SPT you will see that it uses timer 0 to increment CalcFlag once per second. You can change the logging frequency to slower rates, if that suits your application, but 1 reading per second is about as fast as it can go. If your process has a dominant time constant less than say 30 seconds you will have to devise your own method of gathering response data.

Get the logging working

I suggest you make a version of OPENLOOP.SPT where the drive level you set is stored in PIDin, and practice logging on that (i.e. before hooking up to your process).

  1. Connect the SPLat to your computer and launch PIDassist.
  2. Select the "All in SPLat" mode.
  3. Ensure that PIDassist connects by observing the connection status panel. (Hint, if it fails, make sure SPLat/PC is not connected. Exit SPLat/PC if necessary, some versions don't release the COM port)
  4. Set the horizontal display rate (Horiz. S/div) to a fast rate.
  5. Select a log file (.csv) from the file menu (or just use the default)
  6. Enable "Log to file". The green "LED" in the display control pane should start blinking at the logging rate (once per second if you leave my default).
  7. Change the drive level on the SPLat controller. The green trace should change. Leave it for a short while to get some data on disk.
  8. Use Explorer to find the log file and double-click it. Assuming you have Excel installed it will launch and show your data.
  9. Highlight the data and make a line chart. You should see the changes you made to the drive setting.
  10. Go back to PIDassist and observe the error message. What happens is that Excel locks out the file, when it is loaded, and so PIDassist is denied access. If you want to preview a log file make a copy of it in Explorer and view the copy.

It pays to get the logging procedure down pat before you start in on your real process.


PID characterization: Gathering open loop data

At this stage you are going to be driving your process between two fixed levels of input drive. You need to determine drive (control) levels that will drive your process output to a high level short of maximum output and a low level above minimum output. As a suggestion use the normal operating level for one of the two points and something well away for the other. If your process could be operated anywhere in its range, select 10% and 90% or 20% and 80%. Your process drive arrangement will have some form of adjustable input. If it only has an on/off input, like a contactor driving a large heater, then the duty cycle (the percentage of the time it is on) will be the adjustment. Make a copy of OPENLOOP.SPT and alter it to allow manual control of the process and to display the process output. You can then use it to find, by trial and error, the SPLat output settings that give the high and low outputs.

Just to re-iterate: You are looking for two input levels to your process that will produce non-saturated outputs as far apart as practical. Once you have your process interfaced to the SPLat, and you have determined the drive levels that give you a nice pair of non-saturated outputs, you are ready to collect open loop response data. The idea here is to drive the process input between the two drive levels and then record the process output as it varies with time. We will later analyse those records at work out the process's time constant(s).

Call the two drive levels LowDrive and HiDrive.

Here is the procedure for collecting the open loop data. Steps 7-9 are required only if you know for sure that your process is asymmetrical.

  1. Hook everything together, SPLat driving process, process feeding back into SPLat and SPLat being logged by PIDassist.
  2. Set SPLat to LowDrive and let the process stabilise.
  3. Set the log file name to "rising.csv".
  4. Clear the log file and immediately switch the SPLat to HiDrive.
  5. Watch the rising output of the process and monitor it until it has settled at its high output level. You should know by now approximately where it will settle, but expect it to be a bit different (that doesn't matter). Be very sure it has stopped rising before you stop this run. Be on guard for external influences that may affect the final value, like room temperature changes, mains voltage fluctuations etc.
  6. Once it has settled, stop logging. Make a backup copy of the log file.
  7. Set the log file name to "falling.csv".
  8. Clear the log file and immediately switch the SPLat to LoDrive.
  9. Capture the data for decreasing output.
  10. Turn off the process or otherwise make safe.

You now have some data to analyse.

It is very likely that you will have to repeat the above process several times before you get it right. It's called Muphry's Law (if anything will go wrong it will go wrong, including the spelling of Murphy)!


PID characterization: Extracting the time constants

Now we come to the fun part, and the part that will determine the eventual success of the whole exercise: Establishing the time constant(s) of your process. This is where your Excel skills come into play.

In the spreadsheets folder you will find a file called "Rising analysis.xls". Make a copy and load that file into Excel now. The first few rows are reproduced below. This is the actual data I acquired from my contraption. Our objective is to establish the 3 time constants in the top row (Tau). We will start with a practice run using my data.

 | A | B | C | D | E
1 | Tau |  | 450 | 50 | 150
2 | K |  | 0.0018 | 0.0100 | 0.0132
3 |  |  |  |  |
4 | Raw | Normalised | TC1 | TC2 | TC3
5 | 0.19608 | 0.00000 | 0.00000 | 0.00000 | 0.00000
6 | 0.19608 | 0.00000 | 0.00222 | 0.00002 | 0.00000
7 | 0.19608 |  |  |  |
8 | 0.19608 |  |  |  |
9 | 0.19608 |  |  |  |

The first data column is the raw data. It starts at 0.196 and rises to a final value of 0.549 way down the sheet. You will eventually be pasting your data into this column. Because you will have a different number of data points, you will have to reconstruct the other columns. That is why I've left all but the first two rows blank.

Highlight the last 4 cells of data in row 6 (the 2nd data row), i.e. the range C6:E6 and copy it (CTRL+C). Now highlight every cell in column B from B7 down to the last row of raw data, then paste (CTRL+V). This should give you a fully filled in table with 5 columns of data down to row 3514. In doing this you have replicated the formulas for the normalised data and the 3 time constants. The normalised data is just the raw data offset and re-scaled to the range 0-1.

PID open loop rising

Now highlight all the data in columns B through E and do a line chart. It should look like the one reproduced here.

What you have here is the original data as a jaggedy black graph plus the 3 time constant "outputs" in magenta, yellow and cyan.

You now have to manipulate the 3 time constant numbers so one of the graphs overlays the original data as closely as possible. That is an art, not a science, so you are kind of on your own here! Just experiment with the numbers until you get the hang of it.

One trick I have discovered is to approximate the dominant time constant as follows:

  1. Locate the row in column B where the value is close to 0.2
  2. Locate the row in column B where the value is close to 0.7
  3. Calculate the time difference between the two points. This is a good first approximation to Tau1 (cell C1)

Note: If your process is inverting, i.e. its output decreases as the control input increases, swap the names on the template Excel files before you start. In my nomenclature "rising/increasing" and "falling/decreasing" refer to the process input, not its output.

Once you have developed an understanding of how the spreadsheet works, load in your own data. The best way is to make and rename a copy of my file, then delete my raw data from column A. Then load your log file *.csv into Excel, copy and paste column 1 into the new spreadsheet. Click on cell B6 and look at its formula. You will see a reference to cell $A$3514. That was the very bottom cell of my data. When you have your data pasted in you will have to change that reference to the last cell of your data and then re-build columns B through E as outlined above. Now fiddle around to find the time constants.

The exercise so far gives you a set of time constants for rising output. In most cases this applies also to falling outputs. Only in rare and special cases will the rising and falling responses be different. If you have very good reason to believe your process is asymmetrical then repeat the exercise for a falling output.

There is a spreadsheet "Falling analysis.xls" to help you. Delete my data from row 7 down and paste in you data. The process is exactly the same as before. I've "flipped" the data so that even though the output is falling it appears as rising on the graph. I simply found it easier to visualise that way round.

You should now have one or two sets of time constants, one for rising output and possibly one for falling output. Note the time constants and save your spreadsheets. If you have a symmetrical process you will use the same set of time constants for rising and falling.

By the way, going for 3 time constants and not 2, or 5 or 11, is somewhat arbitrary. My experience is that you can get a fairly decent match to the process with 3, and PIDassist supports 3.


PID characterization: Scaling your process

You now have your time constants. The next thing to do is establish the scales for your process and controller. This is simply a matter of determining how many volts into your process translate to how many whatchamacallits out of your process, and how many volts or whatevers they in turn result in at the input of the SPLat. These are all established as steady state values, meaning the process output for a given input, once it has settled (stopped changing).

Lets think a bit about the units we use.

Let's say your process produces "floobies". In fact, you have invented a very nifty flooby generator, or Floobion, which you are rightly proud of, and you need to automate it. The floobion production cell is controlled by a 0-10V control signal, and requires PID control. (There are logic controls as well, which is why you picked a SPLat. You know that once you get into volume production of your Floobion we good folks at SPLat can make you a customized SPLat controller that will be a perfect match for your needs, including a direct flooby cell interface complete with negative warp ion generation. During prototyping you are buying that separately, at great expense). Anyhow, you sit down and make a plot of floobion output versus cell control voltage. It looks like this:

Input V | Output F/hr
0 | 0
0.5 | 0
1 | 10
2 | 130
5 | 500
10 | 1000

If you plot those numbers as a scatter diagram in Excel, you can use "Add Trendline" (right click the curve) to fit a straight line to the data and discover the equation is

F = 105V - 51

Now, you obviously need a flooby sensor to get feedback from your process into the SPLat. The best one you can find has a full range sensitivity of 1500F/Hr for 10V out. With 0F/Hr it gives 1V out, and it is (thankfully!) linear in between. Lets add those facts to our table:

Process I/P V | Process O/P | Sensor O/P V
0 | 0 | 1
0.5 | 0 | 1
1 | 10 | 1.06
2 | 130 | 1.78
5 | 500 | 4
10 | 1000 | 7

Now, PIDassist works with all numbers normalised to the range 0-1. Because we are above working with 10V either end, that means dividing the first and last columns by 10

Process I/P Normalised | Process O/P | Sensor O/P Normalised
0 | 0 | .1
0.05 | 0 | .1
.1 | 10 | .106
.2 | 130 | .178
.5 | 500 | .4
1 | 1000 | .7

Again we enlist the help of Excel and its "Add Trendline" function to find the relationship between process control input and sensor output as

Sensor = 0.6322Control + 0.0691

This last equation is the one we are after, because it gives the final numbers we need to feed into PIDassist. The number 0.6322 is the process (output) gain and 0.0691 is the process output offset.

It is interesting to note that the largest number you can get as an input to the SPLat is 0.6322+0.0691=0.7013 (slightly different to 0.7 because the trendline is a best-fit to a function that is not an absolutely straight line). That is the normalised sensor output at a process output of 1000 floobies/hr. Now, because our controller will eventually be seeking to adjust that value to equal the setpoint, the setpoint should never be set higher than 0.7. That is something to keep in mind when doing simulation runs.

By the way, the fact that the sensor has an offset of 1V or 0.1 normalised unit means that if you want to display the output or the setpoint in engineering units (i.e. Floobies), you will have to do some extra sums in your program. Be very careful how you do this so you don't change the gain seen by the controller function.

It may be that your process is inverting. That means its output decreases when the control input increases. If that is the case you should wind up with a negative value for process gain and an output offset that is equal to the maximum output of the process. In this context the "output of the process" is actually the output of whatever sensor you have measuring floobies.


PID implementation: Modeling your process (open loop)

PIDassist process pane

The process simulator has places for you to enter all the numbers you have now discovered in characterizing your process. It even has a few more!

The TauUp and Taudn numbers are the time constants you established in the spreadsheets. If your process is symmetrical you will have one one set of time constants, in which case you enter the same number for TauUp and TauDn. Enter those now. Gain and offset you determined by measuring the process output and sensor output. If you have an inverting process, "Up" and "Down" refer to the control input.

The MaxOut and MinOut numbers you will normally leave at 1 for MaxOut and 0 for MinOut. Any other numbers would be the result of some special knowledge you have of the internal workings of the process, such as certain stages within the process saturating.

The delay line can be used if you decide that your process has a dead time than cannot be accounted for by simple cascaded time constants. Ideally you will use it only if you have knowledge of the workings of the process that justifies it. Large delays will slow down the simulation. Also, if the process has a dead time approaching or exceeding the dominant time constant, it may be virtually impossible to control with a PID system.

Once you have entered your numbers, save the configuration with a unique file name.

If you like (this is optional but instructive) you can now test the model in open loop mode:

  1. Check the open loop check box (in the Controller pane)
  2. Set the low and high setpoints to two values that will give outputs about the same as you had in the characterisation tests you did on your real system.
  3. Check the Cycle check box.
  4. Set the horizontal scale (Horiz S/div) to approximately equal to the slowest time constant in the target system.
  5. "Unpress" the Pause button. PID open loop simulation

You should get a trace like the one in the above picture. If you log the simulation you should be able to compare the result with the log data from the real process, and get confirmation that your characterization is good.

Don't confuse this with closed loop control. You are simply simulating the open loop tests you did on the real process.


PID implementation: Tuning

At last, we have PIDassist set up to model the characteristics of our target process as closely as we can, and we are ready to start simulating a PID controller acting on that system. The object now is to find the controller settings that give the best control performance as well as a robust solution. Robust means that the control still works OK if the process changes (within reason). Process changes may be a result of wear, different loading, raw material differences, different mains voltages etc.

The technique of adjusting the settings of a controller is called 'tuning'. There's a very large body of experience around on tuning PID loops with linear characteristics, including some very neatly encapsulated sets of rules that make the job pretty easy in most cases. We'll be using one of the most common approaches, referred to as 'Ziegler-Nichols'. I've included web references to a few of others in case you're keen and energetic enough to try them. They use various testing approaches, and produce slightly different results.

The tuning information that follows is for 'linear' systems. Specifically, it is less applicable for control schemes that use 1-bit (bang bang) control. Some of the general principles still apply, but system behaviour under bang-bang control will probably differ from what's described here. With a bit of luck, if your system and controller are linear, the rules in this section will work pretty well. If you controller is bang-bang you can still follow them, but you may have to experiment to a greater degree.

Here are general guidelines that it helps to be aware of.

  • Higher values of Ap and Td correspond to 'more' of the associated action being applied.
  • A LOWER value of Ti corresponds to 'more' integral action being applied. (yes, a bit confusing, but that's the standard approach!)
  • Too much of any of these control actions will probably cause the system to oscillate. This is a condition that must be avoided at all times.
  • As Ap increases in the absence of integral action, offset decreases but the system tends towards oscillation. As Ap decreases, control quality degrades.
  • Derivative generally works only within a fairly narrow range of settings. Too little (Td too low): no effect, too much (Td too high) system tends towards oscillation.
  • Too little integral (Ti too high): offsets reduce too slowly or not at all. Too much (Ti too low), system tends towards oscillation.

On the subject of derivative... Derivative may well not be needed in your controller, and there are significant advantages in leaving it out. The hierarchy in control modes goes something like this:

  • If you only use proportional action (Ti very large or integral explicitly disabled by some means, Td = 0), then your controller will probably 'work' after a fashion, but will most likely settle with an unacceptable persistent offset between the target and actual values. If the dynamics of the process are such that you can run with a fairly high gain, you may not need any integral at all (but this is rather unlikely).
  • If you use proportional + integral (Td = 0), then you could well find your controller thoroughly acceptable for your application. You only have 2 adjustments to make, and they are relatively easy (easier than derivative) to set up. The control response may not be quite as fast as it can be made, but this may not be a problem.
  • If you use all 3 control terms, the potential exists to tune the controller for the best possible response that conventional PID can offer. This may not be a great deal faster than you can achieve without derivative. The penalty you will pay is greater complexity in setting up, and possibly a greater risk of unexpected system instability if your system characteristics change over time.

With occasional exceptions, derivative should not be used:

  • On systems with effectively only one time constant.
  • On level controls.
  • On systems with noisy measurements.

On this question of whether to use derivative or not, well over 80% of the controllers used in major process plants do not use derivative, and don't suffer significantly in consequence.

Start with the following settings, which will result in almost total lack of control;

  1. Horizontal time scale about 0.5 times the longest time constant in the process.
  2. Controller Ap = 1/Process Gain * 0.1 (effectively a very low gain)
  3. Controller Ti = 0 (PIDassist disables integral action if Ti=0)
  4. Controller Td = 0
  5. Sampling time Ts = ideally, less than 1/10 of the shortest time constant in the system. If the system has more than two time constants, this can be stretched a bit, ½ to 1/3 the fastest time constant may be OK.
  6. High and low setpoint values that should give about 20% and 80% of the available range of outputs. What those values are depends on the gain and output offset of the process.
  7. Input quantisation off and output quantisation set to infinite resolution. I think it's best to not clutter the picture initially with quantisation noise.
  8. If your are going to be using an MMi200 or SL99 analog output, enable the output PWM filter.

Uncheck the Closed Loop check box and start the simulation ("un-pause"). Note that the Pause button is automatically activated each time you click on many of the parameter fields.

Now it's time to get going. The principle of this tuning approach, known as "Ziegler-Nichols Continuous Cycling Method" is to deliberately set the process, or its simulation, gently oscillating. The characteristics of the oscillation are measured and used to calculate the appropriate settings for Ap, Ti and Td. Proceed as follows:

Increase the gain progressively, applying a small step in setpoint every time. Until you start to see a bit of overshoot, doubling the gain is a reasonable approach. Once the response starts to liven up, make the gain increments smaller, until a time will come when you will get a nice, steady ongoing swinging. Here are few typical displays as you approach this condition:

PID Z-N tuning initial settings

The display above is with the initial suggested settings. On the next several illustrations the gain is slowly increased to, and beyond the point of steady oscillation

PID Z-N tuning Ap=50

PID Z-N tuning Ap=90

PID Z-N tuning Ap=95

At this stage (once there is some oscillation) I turn off the the setpoint cycling (uncheck the cycle checkbox) and fine tune the gain until I find a setting that gives a steady oscillation that is not saturating the red controller output plot.

PID Z-N tuning fine tuning initial Ap

Note that at this stage, your controller is using only proportional action. If you apply integral or derivative now it will confuse the situation totally.

If your oscillation starts to increase it means that your gain is too high, and if you keep going the amplitude will increase until something limits it. So watch your testing carefully and be prepared to immediately reduce Ap if you see oscillations growing. With a bit or practice, you can steady the oscillation out at a nice, controlled low level with a couple of deft tweaks of Ap.

With a simulation you can't do any damage if the oscillation gets too big, but with a real device maybe you could. If you're new to this game, I really suggest that you do some tuning on PIDAssist before trying it on a real process (that's what PIDAssist is for!) The simulator is a totally safe environment to teach you the touch that is required to edge the process into a gentle controlled oscillation, then to back it out again to good control. It may give you confidence to know that technicians regularly put giant process units such as catalytic crackers in oil refineries into gentle, controlled oscillation to determine tuning settings. If they can do it, you can too!

Once you have your steady oscillation, make a note of the following 'test' variables. For the period, an 'eyeball' approximation (+/- 10%) will do:

Value of Ap, referred to here as Apt

Value of the oscillation PERIOD, referred to here as Pt. This is the time between two consecutive up (or down) peaks, as in the following diagram (I've altered the display time scale to stretch out the period on the screen):

We have about 2.5 horizontal divisions at 125S/div, so Pt = 2.5 * 125 = 312.5S (Hint: For better accuracy measure the period of 5 cycles then divide by 5.

You are now pretty close to done! The next step is to calculate the values for the 3 tuning parameters that, hopefully, will give you a good response. These values are as follows, depending on whether you plan to use Derivative or not:

 | Derivative not included | Derivative included
Ap | Apt * 0.45 | Apt * 0.6
Ti | Pt / 1.2 | Pt / 2
Td | - | Pt / 8

You now just enter these settings into your controller, and apply another step. If everything is going to plan, what you should see is something like this:

PID Z-N tuned PI no D

The above diagram is the tuning result without derivative action. The one below has the settings with derivative action.

PID Z-N tuned PID

From the perspective of formal 'tuning', that's it. It's taken a while to explain, but once you get the hang of it you'll find it's easy. Of course if your process has time constants of hours, it will take that long on the real process :-). One other reason to get serious about PIDAssist.

Now that you've achieved a reasonably acceptable response, your next step is to do some 'seat of the pants' tweaking. It's common to reduce the gain (Ap) by maybe 10 – 20% to increase the margin between normal operation and potential instability.

During testing, you'll find the undo function in PIDAssist useful in backing out of unsuccessful test runs quickly.

Watch the actual numerical output values in the display pane (raw top, filtered below). It sometimes happens that the output response looks beautiful, but the actual values are wrong. That can happen especially if the controller output is saturating.

Once you have established parameters that give a good result, you need to look at input and output quantisation.

If you are using an analog input on the SPLat, turn on input quantisation. This may very well result in more noise, especially in the red controller output. Some noise will do no harm (unless maybe you are using an electromechanical relay as a driver, in which case you will want to minimise plain old mechanical wear). However, if the output saturates a lot the noise can produce a rectification effect and result in a shift in the average process output (so watch it!).

You will almost certainly need to test output quantisation. Pick the output settings that best match your application.

Finally you need to test your settings for robustness. That means you check that for all likely changes in the process parameters such a gain and major and minor time constants the control system will continue to perform acceptably. This will involve some careful thinking about and analysis of your process. For example, ±10% variation in mains voltage applied to a heater will produce ±20% variation in heater power, which would translate directly into similar gain changes. Think differently about normal and abnormal changes. Your system should stay within its specs for normal variations. For abnormal variations, beyond what it is specified to work with, you should at least aim for graceful and hazard-free degradation. In that analysis include possible silly things an end-user might do to your system. That is where integrating the logic control aspects of SPLat into the PID control may come into play.

When you are happy with the simulation results, save the configuration (*.pid) file. It is precious!


PID implementation: Interactive control

Now you are at the stage where you have characterized your process and you have a *.pid file with the PID control setting that will (hopefully!) work with your process. Time to move on to The Real Thing!

Fear not, there are still a few tricks left in PIDassist to help you on your way.

In the folder /SPLat Programs under PIDassist you will find a SPLat program called Interact.spt. Make yourself a copy of that program and load it into SPLat/PC. I will assume that by now you have mastered how to switch access to the SPLat board between PIDassist and SPLat/PC by suitably connecting, disconnecting and isolating the COM port. I will also assume that you have got the SPLat interfaced to your process.

Now would be a good time to review your scaling and inversion. If you get the inverting/non-inverting setting wrong, you could wind up with a mess. Most likely the closed loop system will latch up at zero output or full output. If you fluke a very low initial error it could also just sit there doing nothing, so don't take the absence of latchup as a sure sign you have it right. The other gotcha to watch out for is a process that can suddenly "change sign". This can happen sometimes if an input gets over-driven. For example, some FET operational amplifier chips will change sign if over-driven.

You must have a very thorough understanding of the process you are controlling, including its behaviour under extreme conditions of loading, temperature, over-drive, mains voltage fluctuations, poor feed stock etc. etc.

Interact.spt is a complete PID control program designed to interact with PIDassist. It will control your process from the SPLat, but let you change all the control parameters from PIDassist. It will also let you display and log results to disk. Take a close look at the program now. There are a number of sections of code that you may need to tailor to interface it to your process. My code includes some fancy stuff for monitoring what's going on with MMi200 front panel LEDs and LCD. It's up to you if you do something similar or apply KISS. I find it invaluable to have close monitoring of as much stuff as possible. When modifying the program, make very sure you don't interfere with any of the RAM addresses I have flagged as supplied by the PC or read out by the PC.

When you are modifying my program, consider adding a push button the will force the integral value and the output to a level that is safe for the process. You could even have a button to pause/unpause the PID calculations. PauseFlag, by the way, is set to the state of the PIDassist Pause button (True = paused), but I haven't put any code for it in Interact.spt.

You need to select the "All in SPLat" mode of PIDassist.

The parameters from the Controller pane of PIDassist are automatically transferred to the SPLat at the appropriate times. This generally means when you "un-press" the Pause button after editing some values, or immediately for those parameters that don't Pause the program when changed. Undo also updates the SPLat.

Changing parameters during a run can produce "bumps" in the output. Do not do it if there is any danger of damage or injury if your process gets out of control. In general, you must take every precaution during this whole procedure to prevent any dangers arising from your process getting out of control. You must have safety devices in place that will independently shut down the process before anything bad happens. This is not the time to bypass pressure relief valves, thermal overloads or emergency stop switches.

Once you have all your safety precautions in place, start the SPLat program running and click the Pause button 2-3 times to make sure the parameters are sent to the SPLat. Then apply power to your process, i.e. do whatever it is you do to enable it. If you wish, turn on logging as well.

The SPLat should now be controlling your process. If it's a slow process, you will have to be patient to see the results develop. Change the setpoint to see how well it handles the change. You can use the SetPoint Cycle function if you want.

Superimposed real and simulation.gif

The graphs show my results with the heating contraption, comparing the simulation (blue and yellow) versus The Real Thing (red and green). I did a bit of screen capturing and superimposing in an image editor to create the composite curve. For this run I used PID settings that deliberately produced slight ringing, just to make it more interesting. You can see my process gain and offset are a bit off, but overall the two are in good agreement.

If necessary you can now fine-tune your PID control parameters. Certainly be prepared for some difference between the simulation and the real thing. There are a host of subtle real world factors that can cause this to occur. If you're lucky it will be just a matter of a minor tweak in Ap, maybe not even that. At the other extreme, you could find that a complete retuning with the live system is required. If this happens you should look for things that might have caused the difference. I suggest you get lots of practice in pure simulation mode, so you can get a bit of a feel for what effect the various parameters have. They do interact a lot, so it can be a challenge!

This is also the time where you should thoroughly go through the full range of operational conditions that your device will encounter. Try doing nasty things to the process. Change the supply voltage, make sudden changes in the loading (how much heat, energy or floobies are taken out), change the ambient temperature. In short, do all the things it will experience once it is put into operation. Then do some things beyond what you expect it to normally encounter (if you don't your customers probably will!). It may be a lot cheaper to fix a problem now than later.

Save your settings in a .pid file, and you are ready to generate your final (production) SPLat code!


PID control: SPLat programming

We're in the home straight now, at least as far as the PID control part of your application is concerned. You have interfaced your process to the SPLat and you have all the settings to make the PID control work. You've probably had a few false starts along the way, that's perfectly normal, but if you've paid enough attention to the detail you are now staring a great solution in the face.

All that remains for the PID function is to generate the production PID code and integrate it into the rest of your SPLat program.

PIDassist has a built in code generator. Use the .pid settings you determined in the previous chapter. They will provide most of the information the code generator needs. The only other decision you need to make are:

RAM Addr

This setting lets you decide where the code generator locates the RAM variables it needs. The number of RAM bytes required will depend on the parameters chosen.

Self Timed

If you check this box, the PID code generate will include the use of timer 7 to generate sampling intervals. It is then up to you to call (GoSub) the PID_Calc subroutine frequently to give it a chance to do its sums at the appropriate times. If you do not check this box then you must take responsibility for calling PID_calc exactly every sample time.

In practice there are inevitable delays in detecting that a timer has timed out, so you will end up calling PID_Calc at a slower rate than intended. Dialect 12 of the SPLat language will contain a new timing mechanism that can mitigate this effect.

Fixed SP

Checking this box will result in code that contains the setpoint value from the Controller pane hard-coded.

The setpoint is always interpreted as the input (usually voltage) actually presented to the SPLat board, irrespective of inversion.

Once you have all the setting the way you want them, click the "Generate SPLat code" button. You can then copy and paste the resulting code into your own SPLat program and add your own code to link it into your application. The generated code is not designed to interact with PIDassist.

The generated code can vary quite a bit, depending on your settings.


Tutorial: Semaphores

This tutorial covers the theory and use of semaphores in SPLat.

Semaphores allow you to greatly increase the effective amount of RAM in your SPLat board. Semaphores are the most efficient way of storing simple logical on/off, true/false, yes/no enable/disable information. Prior to semaphores you would usually use up a whole byte of memory to store a logical value. With semaphores it is easy to get 8 logical values per byte. That means you can have a large number of logical variables in your program and still only use up a fraction of the available RAM.

Previous Topic

Next Topic

In This Chapter

Semaphores: The quick and easy way

Semaphores: Simplified addressing rules

Semaphores: Bits and Bytes

Semaphores: Programming examples


Semaphores: The quick and easy way

Automatic RAM Allocation makes the use of semaphores very easy. With auto-allocation, you simply declare semaphores using the defSEM directive, and SPLat/PC takes care of the rest. If you are simply setting, clearing and testing single semaphores, this is as far as you need to go with this tutorial. If you want to do advanced things like simultaneously manipulating several semaphores (bits) in a byte or using indexed addressing you will need to complete the rest of this tutorial.

See also the Basic and Intermediate examples in the Examples section


Semaphores: Simplified addressing rules

There may be times when you decide to use some of the more sophisticated techniques with semaphores, and the automatic memory allocation won't work. In those cases you will need to manually allocate them to known RAM bytes and bits. Examples of when you would need this are:

  • Forcing semaphores to live in Permanent Memory
  • Locating them in known addresses for the sake of external communications with SPLatLink
  • Using indexed addressing of semaphores
  • Using various advanced techniques where the actual addresses must be known.

We have made the semaphore feature in SPLat as flexible as possible, so it is able to use every byte of RAM for semaphores, and they can be used in sophisticated ways. However, for a vast majority of applications you would only need a handful of semaphores, used in a straightforward manner, so you can ignore some of the complexity that invariably comes with the flexibility. This page gives you a simple strategy to use in such cases.

All the semaphore instructions have an argument field that includes the semaphore number ss and a base address bb. In our simple strategy the base address is ignored, and defaults to 0. That means the first 32 bytes of RAM are potentially available as semaphores. In this strategy, though, we actually only use 8 bytes (0 through 7) for semaphores. That will give you 64 semaphores, numbered 0 to 63. As it happens (in fact, it was deliberate planning from day one!), RAM locations 6 and 7 are part of the permanent memory range, so two bytes (16 semaphores) of the designated semaphore area can be saved with PermStore.

The strategy is now defined by the following rules:

  1. Never allocate RAM locations 0-7 to any other variables.
  2. Define any semaphores you want in the range 0-63
  3. Use semaphore numbers 48-63 for any semaphores you want in permanent memory (you still have to do PermStore and PermRecall instructions at the appropriate times!)
  4. When you use semaphore instructions, simply leave out the bb part of the argument.

If you will never need more than 64 semaphores in your application, that's it, no need to read on


Semaphores: Bits and Bytes

Each of the bytes that makes up the RAM memory in SPLat consists of 8 bits. Bit stands for BInary digiT.

One bit is the electronic equivalent of an on/off switch. It can represent just two values, on and off (or yes/no or true/false). Very often the two values are referred to as the numbers 1 and 0.

There are 8 bits in a byte. Why 8 was picked as the magic number is partly historic, partly technical, but that's how it is. By convention the bits in a byte are numbered 0-7, starting from the right:

7

6

5

4

3

2

1

0

Similarly, the bytes within RAM memory are numbered from 0 up, so the first byte is 0, the 2nd is 1, etc. The number of a byte is called its address.

Consider the following grid, which represents the first few bytes of memory.

 | Bit number
Byte address | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
0 |  |  |  |  |  |  |  |
1 |  |  |  |  |  |  |  |
2 |  |  |  | X |  |  |  |
3 |  |  |  |  |  |  |  |

The bit marked X is bit 4 in byte 2

Imagine now if we start at the beginning of RAM and number the bits sequentially, not starting back at 0 for each byte.

Byte address | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
1 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8
2 | 23 | 22 | 21 | X | 19 | 18 | 17 | 16
3 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24

The bit marked X is bit 20 in this new numbering scheme.

Imagine now if we shift the point where we start counting bits from byte address 0 to byte address 2. We get the following table:

Byte address | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
0 | ? | ? | ? | ? | ? | ? | ? | ?
1 | ? | ? | ? | ? | ? | ? | ? | ?
2 | 7 | 6 | 5 | X | 3 | 2 | 1 | 0
3 | 15 | 16 | 14 | 13 | 12 | 10 | 8 | 8

The X bit has now become number 4. The bits in bytes 0 and 1 no longer have numbers in this scheme, because negative numbers are too hard to contemplate.

What we have done is to move the base address from byte 0 to byte 2. Every semaphore instruction in SPLat allows you to specify a base address as well as the bit number. However, the base address is an optional argument, and if you leave it out it defaults to 0. That means you can happily ignore it if you are content with accessing just the first 32 bytes of RAM (256 bits) as semaphores. If you do use the base address you will be able to get up to 1024 semaphores in 128 bytes of RAM, and you will be able to do interesting things like indexed addressing of semaphores.

Obviously, if you explicitly state the base address you will have to do some advance planning of where you are going to place particular semaphores, both in terms of absolute memory address and relative to the base address you intend to use in your instructions. This applies doubly if you are going to index the base address.


Semaphores: Programming examples

In this section we will present some examples of how semaphores might be used. Each example highlights some particular way in which they can be used, but is not a complete working program.

Previous Topic

Next Topic

In This Chapter

Example (Basic): Setting and testing semaphores

Example (Intermediate): Combining semaphores (Boolean processing)

Example (Advanced): Using semaphores to latch inputs

Example (Advanced): Combining semaphores and Branch instructions

Example (Advanced): Indexing semaphores


Example (Basic): Setting and testing semaphores

The simplest (and most common) use for semaphores is simply to have your program remember some on/off or yes/no item of information for later action. If you are accustomed to relay logic, this is equivalent to using an internal relay to hold onto a bit of information or an intermediate result.

Semaphores use RAM memory, and memory must be set aside for them. The easiest way to do this is with the defSEM directive.

Example 1:

sCooling        defSEM
sFillLow        defSEM
sFillHigh       defSEM
sOverTemp       defSEM

The following subroutine CheckLevel would return the state of a low, medium or high level probe depending on two of the above semaphores:

CheckLevel:     GoIfST          sFillLow,CheckLow    ;Go to CheckLow if low semaphore set
                GoIfST          sFillHigh,CheckHigh  ;Go to Checkhigh if high semaphore set
CheckMedium:    Input           iMediumProbe         ;By default must medium
                Return
CheckLow:       Input           iLowProbe
                Return
CheckHigh:      Input           iHighProbe
                Return

Example 2:

Semaphores can be set and cleared in several ways. They are directly set and cleared using SetS and ClrS instructions. For example, to set or clear the sFillHigh semaphore depending on whether the button on input iHighFill has been pressed or not:

                SetS            sHighFill          ;Assume switch pressed
                GoIfInK         iHighFill,HighFill ;Test the switch, skip next inst if pressed
                ClrS            sHighFill          ;Switch was not pressed ... reset semaphore again
HighFill:

Example 3:

You can also change a semaphore by storing the outcome of a previous operation (in the X register) to it. In that case any non-zero result will set the semaphore, and a zero result will clear it. Using the StoreS instruction, the previous example can be simplified:

                InputK          HighFill          ;Read the switch
                StoreS          sHighFill         ;Save result

If you are using MultiTrack, SPLat's easy to use multitasking, you should also look at semaphores for task synchronization.


Example (Intermediate): Combining semaphores (Boolean processing)

Semaphores can be combined using the Boolean (logical) instructions AND, OR, NOT and XOR. So long as you stick to those instructions, and not the "masked Boolean" AndM, OrM, NotM and XorM, you will get the expected results. If you are curious about why this is so, the answer is in the definition of the RecallS instruction.

For example, turn on output oValve only if (Vessel is full AND Cycle is complete) OR pressure is high (each of the conditions is represented by a semaphore that has been calculated somewhere else in the program):

        RecallS     sVesselFull     ;push sVesselFull onto stack
        RecallS     sCycleComplete  ;push sCycleComplete onto stack
        And                         ;AND the first two terms
        RecallS     sHighPressure   ;push sHighPressure onto stack
        OR                          ;OR in the third term
        Output      oValve          ;Send the outcome to the valve

To understand this code fragment you need to understand how the register stack works. That is covered in the SPLat/PC helpfile.


Example (Advanced): Using semaphores to latch inputs

In some applications there is an inherent hazard in reading an input several times in succession, because of the danger of the input changing between reads. Suppose for example we have an application which must decide to either fill or drain depending on the following conditions (X means "don't care")

Input A | Input B | Input C | Action
ON | ON | X | Fill
OFF | OFF | ON | Drain

We might write the following code:

QryFill:  Input     A         ;Read A
          Input     B         ;Read B
          AND                 ;AND them together
          GoIfT     Fill      ;Test result, Fill if true

QryDrain: Input     A         ;Read A

          NOT                 ;Want A off, so invert

          Input     B         ;Read B

          NOT                 ;Want B off, so invert

          AND                 ; A' and B'

          Input     C         ;Read C

          AND                 ; A' AND B' AND C

          GoIfT     Drain     ;Test result, drain if true

This will work correctly ... 99.9% of the time. However, if B and C are on, if A should be off the first time it is read, and then manage to turn on before the second read (5th line), then the program will neither fill nor drain. This is the kind of coding 'gotcha' that leads to pesky, virtually uncatchable bugs.

Now, you may well spot some way of fixing this up without using semaphores. In fact, consider that as an exercise! However, the following use of a semaphore would also work:

         Input     A         ;Read A

         StoreS    SaveA     ;Save it in a semaphore

         Input     B         ;Read B

         StoreS    SaveB     ;Save it in a semaphore

         RecallS   SaveA     ;Use the latched version of input A

         RecallS   SaveB     ;Use the latched version of input B

         AND                 ;AND them together

         GoIfT     Fill      ;Test result, Fill if true

         RecallS   SaveA     ;Use the latched version of input A

         NOT                 ;Want A off, so invert

         RecallS   SaveB     ;Use the latched version of input B

         NOT                 ;Want B off, so invert

         AND                 ; A' and B'

         Input     C         ;Read C

         AND                 ; A' AND B' AND C

         GoIfT     Drain     ;Test result, drain if true

In this code we only read A once, then save it in a semaphore. After that it doesn't matter if the actual input changes, because we will be using the one latched sample in our decisions.

A technique that you can use if your program is structured as one great big loop that executes repeatedly (see the multitasking tutorial), is to sample all inputs at once at the top of the program loop, something like this:

MainLoop: InputFM   0           ;get inputs 0-7

          Store     InputSems0  ;Store in the 1st input semaphore byte

          InputFM   8           ;get inputs 8-15

          Store     InputSems8  ;Store in the 2nd input semaphore byte

          InputFM   16          ;get inputs 16-23

          Store     InputSems16 ;Store in the 3rd input semaphore byte

               ; ... etc ...

          RunTasks

          GoTo      MainLoop

Within each task any reference to inputs would actually access the stored values via semaphore instructions:

Task_1:   GoIfST    Sensor,InputSems0,GotSensor

         ; ... etc

What is happening here?

The first InputFM instruction reads the states of inputs 0 through 7 as a single byte of data, one bit per input, into the X register. The Store InputSems0 then saves that byte (8 bits/ 8 inputs) in RAM location InputSems0 (which you must define in an mEQU). Later on, in line Task_1 we test a semaphore Sensor counted from base address InputSems0. If say Sensor is sEQUated to 3, what we are actually testing is the (semaphore representing the) state of input 3 as it was when we sampled it at MainLoop.

The above explanation is for inputs 0-7. Inputs 8-15 are read with an InputFM 8 and saved in semaphore byte InputSems8. When you subsequently access the semaphores you have a choice of what base address you use in the semaphore instruction. If you use InputSems8 as the base address, you will have to use semaphore numbers 0-7. If you use InputSems0 as the base address, you will have to use semaphores 8-15, providing InputsSems8 follows immediately after InputSems0 in RAM. If this statement is not clear to you, go back and re-study the description of semaphore addressing.


Example (Advanced): Combining semaphores and Branch instructions

This example follows on from the previous one, to give you an interesting way of testing and branching out depending on several semaphores. Imagine you have 3 semaphores, semA, semB and semC. These may represent inputs or the results of previous calculations. You now need to do one of several things depending on the semaphores. The following table summarizes what your program must do:

C | B | A | GoTo ... | N
0 | 0 | 0 | DoIT | 0
0 | 0 | 1 | CanIt | 1
0 | 1 | 0 | GoAway | 2
0 | 1 | 1 | DoIt | 3
1 | 0 | 0 | GoAway | 4
1 | 0 | 1 | CanIt | 5
1 | 1 | 0 | OhDear | 6
1 | 1 | 1 | Oopsy | 7

By careful planning we locate the 3 semaphores in bits 0, 1 and 2 of a single byte MySems. Here are the directives that define them:

MySems    mEQU       25       ;Where-ever in memory you want your semaphores stored
semA      sEQU       0
semB      sEQU       1
semC      sEQU       2

If we interpret those 3 bits as a binary number, they can be taken as a number N between 0 and 7 (i.e. 8 numbers). This represents exactly the 8 possible permutations of 3 semaphores. If you are unfamiliar with binary numbering you can find plenty of information online.

The following code will implement the above table in a compact Branch/Target structure:

          Recall      MySems
          LoadX       %00000111  ;Mask to select bits 0-2
          AndM                   ;Select the 3 semaphores
          Branch
          Target      DoIt       ;N=0
          Target      CanIt      ;N=1
          Target      GoAway     ;N=2
          Target      DoIt       ;N=3
          Target      GoAway     ;N=4
          Target      CanIt      ;N=5
          Target      OhDear     ;N=6
          Target      Oopsy      ;N=7

This code is both compact and very easy to maintain.

Exercise 1: Write the code to perform the same function without using a Branch/Target table.

Exercise 2: Modify the Branch/Target table so the program goes to Oopsy whenever A and B are both on.

Exercise 3: Modify the program in exercise 1 so it goes to Oopsy whenever A and B are both on.

For those of you with a chip level design bent, this method is really the same idea as using a 1-of-8 (74HC138) or a 1-of-16 decoder chip (74HC154) to generate boolean equations.


Example (Advanced): Indexing semaphores

Some semaphore instructions can be used with the index register. This allows you to devise quite sophisticated schemes. The most likely use for indexing would be where you are writing a program that works on several identical channels, like maybe a 4-channel lighting control system.

The advantage of using indexed addressing is that the same code can be used for all 4 channels, with a huge reduction in the size of the code. The alternative would be to have 4 almost identical copies of the program running simultaneously. That can certainly be done, but becomes a headache if you need to make any changes.

The down-side is that the code becomes more complicated, and in particular that the planning of memory storage becomes absolutely crucial to the success.

In indexed semaphore instructions the index register is added to the base address argument (not to the semaphore number). That fits in very nicely with the concept that you use the index register as the channel number. Here are the memory allocation directives for a hypothetical 4-channel lighting control system:

;Start the indexed memory at location 76 (arbitrary!)
Sems0       mEQU      76      ;Room for 8 semaphores
Sems1       mEQU      77      ;Room for 8 semaphores
Sems2       mEQU      78      ;Room for 8 semaphores
Sems3       mEQU      79      ;Room for 8 semaphores
CycleCount0 mEQU      80      ;A single byte variable
CycleCount1 mEQU      81      ;A single byte variable
CycleCount2 mEQU      82      ;A single byte variable
CycleCount3 mEQU      82      ;A single byte variable

;Define the semaphore bits

semA        sEQU      0

semB        sEQU      1

semC        sEQU      2

semD        sEQU      3

semE        sEQU      4

;Define outputs

Lamp0       oEQU      10

Lamp1       oEQU      11

Lamp2       oEQU      12

Lamp3       oEQU      13

Given these definition, the following subroutine code will count down any non-zero cycle count for those channels whose semaphore D is true and turn on its output if it hits zero (probably a total nonsense operation, but what the heck!).

Nonsense:

            LoadI     4            ;Easier to work backwards, gets DEC'd B4 1st use

NonsLoop:

            ItoX                   ;Test index register

            RetIfZ                 ;R/ just did 0

            DecI

            iRecallS  semD,Sems0   ;This will access bit D of byte Sems0+Index

            GoIfF     NonsLoop     ;Loop back if the semaphore is false

            iRecall   CycleCount0  ;Get the I'th cycle counter

            Push                   ;Make a duplicate

            GoIfZ     NonsLoop     ;Loop back if already at zero

            DecX

            Push                   ;Make a duplicate

            iStore    CycleCount0  ;Save the new value

            GoIfNZ    NonsLoop     ;Loop if non-zero

            iON       Lamp0        ;turn on the lamp

            GoTo      NonsLoop     ;Go and process the next one

 A couple of points about this code:

  • It is easier to do the loop as a count down loop with a test for 0 than a count up loop with a test for 3.
  • It's easiest to do test for completion at the top of the loop, because the code loops back to that common point from 3 separate places. Hence, I is initialized to 4 rather than 3, because it gets decremented once before it is used for the first time.
  • While several instructions refer to the channel 0 datum, they are all indexed so they actually access the I'th channel's data.


Tutorial: Using SPiceX for large I/O counts

This tutorial will teach you how to control a large number of XPice expansion boards like the XBIO16 and XIRO16. This will allow you to configure SPLat control systems with I/O counts of 200 or more. The method you will learn is called SPiceX, and is one of many expansion schemes defined in the SPLat Expansion Framework.

Background

The expansion boards like XBIO16 are designed to work under an expansion scheme called XPice, and they are referred to generically as XPice expansion boards. When you connect one or two XPice expansion boards to a SPLat controller, the controller is capable of sensing the presence of those boards and adjusting its input and output drivers accordingly. Once it has done that it provides full support to the expansion boards and they appear to your SPLatWare program, and to SPLat/PC, as a number of regular I/O points. However, providing that transparent support consumes resources within the SPLat processor, both processor time and memory. Therefore there is a limit to how many expansion boards can be supported in this way, that limit being determined by the amount of resources we are willing to set aside for expansion board support.

The limit we have set is that there can be a maximum of 32 outputs and 32 inputs supported on expansion boards with full I/O transparency. That corresponds to 4 XIRO16 boards or 2 XBIO16 boards.

Breaking the limit

This tutorial will show you how to exceed the limit described above. The programming is more complicated, so this rates as an advanced topic, but if you are comfortable with bits and bytes and prepared to forego some of the debugging conveniences provided by SPLat/PC, it does give you a way of building systems with high I/O counts. The method is particularly suited to applications where you have for instance regular arrays of basically identical outputs, for example display panels or light chasers.

Previous Topic

Next Topic

In This Chapter

SPiceX: Basic principles

SPiceX: Initialization

SPiceX: Interaction with normal I/O updates

SPiceX: Interaction with SPLat/PC

SPiceX: Input and output mapping

SPiceX: Programming examples


SPiceX: Basic principles

The basic principle of this "SPiceX" technique is that you maintain an image of the expansion inputs and outputs in RAM (data memory) and then bulk transfer them between the controller and the expansion board using special instructions. Within RAM each input and output point will be represented by a single bit of data. Hence, to for example change an output pin you would first alter the corresponding bit in RAM (the semaphore instructions can be very handy for this!) and then you would transfer the RAM data to the actual expansion boards.

Note that with this technique it is not possible to update a single output pin (or read a single input pin) individually. You must update (refresh) the whole lot (every bit in every expansion board) every time. That's why you keep an image of all outputs in RAM, and transfer them all out every time you need to update even one single output pin. In theory you could access inputs pseudo-randomly by doing a total read each time and picking out the bit you want. In practice it usually is simpler to read in the whole lot periodically and then pick the bits you want out of RAM.

The principle instruction used for transferring data between the controller and the expansion board is called SPxTxfrU. That instruction transfers a number of bytes out of the U register and out to the expansion boards (as outputs) and at the same time transfers the same number of inputs from the boards and leaves them in U. The number of bytes transferred is set by the number pre-loaded into the V register. The transfer always starts at U0.

The U register is described under "SPx Register Model" in the SPLat Expansion Framework documentation. In brief it is a register consisting of 20 bytes of information (this may grow in the future). It is used for a variety of data transfers between the SPLat controller and external devices, and in the future is likely to find other uses. Associated with U is the V register. V is a single byte register that is usually used as a byte counter to determine how many bytes of U are being transferred.

The SPxTxfrU instruction has an argument p. In all existing implementations use p=0, i.e. SPxTxfrU 0.


SPiceX: Initialization

The data transfers between the controller and the expansion boards uses a serial clocked transfer method. We use a slightly modified version of the Motorola SPI (Serial Peripheral Interface) bus. To ensure maximum future versatility we have provided for a few variations on how data is transferred. Before you can start using SPxTxfrU instructions you must be sure the transfer mechanism is initialized to the correct mode. The instruction:

SPxMode m

does this. 'm' is the mode. For our purposes the available modes are 4, 5, 6 and 7. These correspond to 4 different bus clock (speed) rates of 1.8MHz (m=4), 920kHz (m=5), 230kHz (m=6) and 58kHz (m=7). The default mode is 6, and is the one you would normally use. Current boards are no capable of working at all in mode 4.


SPiceX: Interaction with normal I/O updates

If you are using the SPiceX method for updating standard expansion boards (XBIO16, XIRO16, FP16, HIO16, MBIO16), you somehow need to prevent the controller from trying to also perform "normal" I/O updates (which would conflict with what you are trying to do). Here are the rules for how this is handled with standard expansion boards:

  • SPiceX can only be used if there are more than 64 XPice I/Os (4 XBIO16 boards or 8 XIRO16/HIO16 boards or equivalent). If SPLat/PC recognizes and configures itself to the expanded configuration, it won't work in SPiceX mode.
  • Executing an SPxTxfrU or SPxMode instruction will switch the board to SPiceX mode only if the previous condition is met.
  • Inputs are inverted, i.e. an ON input becomes a logical 0/False in RAM
  • Connecting to SPLat/PC after SPiceX mode is enabled will turn off SPiceX mode improperly. If there are 64 or fewer XPice I/Os SPLat/PC will show them as regular I/O but attempting to activate them from the SPLat/PC screen may produce erroneous results.


SPiceX: Interaction with SPLat/PC

SPLat/PC is not able to simulate SPiceX transfers.


SPiceX: Input and output mapping

When you use the SPiceX transfer method you need to know which physical I/O bits on the expansion board(s) correspond to (map to) which bytes and bits within the U register.

The XPice expansion boards are built around 8 bit shift registers. These shift registers are always used in pairs. One of the pair provides 8 output bits and one provides 8 input bits. On 8 in/ 8 out boards like the XIRO16 there is just one such pair. The XBIO16 on the other hand has 2 pairs, giving 16 inputs and 16 outputs. However, the inputs and outputs are connected together on the board to yield 16 bidirectional I/O pins, saving on connectors, indicator LEDS and board space. When you connect several boards together you are actually connecting their shift registers together into two long chains.

When you execute a SPxTxfrU instruction, data gets shifted out of U into the output shift registers, at the same time as data is shifted from the input shift registers into U. The direction of shifting is out of U0, bit 0, and in to Un-1, bit 7 (n= the number of bytes involved). When the shifting is finished, the data that was in U0 will be in the output shift register that is electrically furthest from the controller. At the same time the new data in U0 will be the input data from the input shift register that is electrically closest to the controller. That means the input and output data are in reverse byte order relative to each other. Note however that the bit order within each byte is always the same, i.e. not reversed.

The following table shows the mapping for four XBIO16 boards. Each XBIO16 board has 16 bits, each of which can be used as an input or as an output. These are numbered 0-7 for the first byte's worth, and 8-15 for the second byte's worth. The table shows for each of the 4 boards where the data associated with each byte will end up in U if it's used as an input, or where in U it will originate from if used as an output. Board 1 is the board electrically closest to the controller, i.e the one whose SPxIn connector is connected to the controller.

4 x; XBIO16 | Board 1 |  | Board 2 |  | Board 3 |  | Board 4
 | 0-7 | 8-15 | 0-7 | 8-15 | 0-7 | 8-15 | 0-7 | 8-15
Inputs | U0 | U1 | U2 | U3 | U4 | U5 | U6 | U7
Outputs | U7 | U6 | U5 | U4 | U3 | U2 | U1 | U0

The next table show the situation for three XBIO16 boards. Because of the byte order reversal, and because U0 must always be represented, this table is not just the previous table with the board 4 data chopped off.

3 x; XBIO16 | Board 1 |  | Board 2 |  | Board 3
 | 0-7 | 8-15 | 0-7 | 8-15 | 0-7 | 8-15
Inputs | U0 | U1 | U2 | U3 | U4 | U5
Outputs | U5 | U4 | U3 | U2 | U1 | U0

You should study these tables until you can clearly see the pattern.

The next table shows the situation for a system with six XIRO16 boards. It is remarkable similar to the case of three XBIO16 boards.

6 x; XIRO16 | Brd1 | Brd2 | Brd3 | Brd4 | Brd5 | Brd6
Inputs | U0 | U1 | U2 | U3 | U4 | U5
Outputs | U5 | U4 | U3 | U2 | U1 | U0

From the above you should be able to work out the mapping for your particular setup, including a mixture of board types.


SPiceX: Programming examples

Previous Topic

Next Topic

In This Section

Example: Identifying SPiceX outputs

Example: Identifying SPiceX inputs

Example: SPiceX and ModBus


Example: Identifying SPiceX outputs

This example program is handy for familiarizing yourself with the mapping of the SPiceX outputs.

;IDENTOUT.SPT : Sample program to identify which outputs get
;activated from which bytes/bits of U when using the
;SPxTxfr instruction.

;The program sends (up to) the first 8 bytes of RAM

;out to a chain of (say) XBIO16 boards. Byte 0 is counted

;up in binary. The other bytes are statically preloaded

;with their own address, i.e. byte 5 contains binary 5.

********************************************************************

;Set the following constant to reflect the number of bytes of

;data being shifted out.

;

;       XBIO16          2 bytes

;       MBIO16          2 bytes

;       XIRO16          1 byte

;       HIO16           1 byte

;       FP16            1 byte

ShiftLength     EQU             8       ;Minium 5 with standard expansion boards!

**** NOTE ****

;SPiceX mode works only with standard XPice expansion boards when

;there is more than 5 bytes of data. For less than that the

;automatic detection will sense the boards and map them as

;regular I/O.

;With custom expansion boards it may work with fewer than 5 bytes,

;depending on the design of the board. Please consult the board documentation

****************************************************************

                SPxMode         6

                SetMem          0,0             ;Pre-load RAM images

                SetMem          1,1

                SetMem          2,2

                SetMem          3,3

                SetMem          4,4

                SetMem          5,5

                SetMem          6,6

                SetMem          7,7

                SetV            ShiftLength

A               MemToUV         0,0             ;Transfer V bytes from RAM to U

                SPxTxFrU        0               ;Send it out

                Pause           10

                IncM            0               ;Increment counter

                GoTo            A


Example: Identifying SPiceX inputs

This example program is handy for familiarizing yourself with the mapping of the SPiceX inputs.

;IDENTIN.SPT : Sample program to identify which inputs get
;reflected in which bytes/bits of U when using the
;SPxTxfr instruction.

;The program senses just one expansion input turned on

;at a time. It reads in up to 8 bytes of input data using SPxTxfrU

;and saves them in RAM. It then inspects the RAM to find a set bit.

;Finally it displays the byte/bit number in onboard outputs

; 0-7. Bits 3-6 contain the byte number. Bits 0-2 contain the

;bit number within that byte. Bit 7 is BLINKed to signal the presence of data

;(otherwise byte 0, bit 0 would show nothing).

;You can also inspect the raw data using SPLatLink (The SL button

;in the Module window of SPLat/PC).

;Data is in locations 0 up, inverted. Click the Format button in the

;Data memory window to get binary formatting.

****************************************************************

;Set the following constant to reflect the number of bytes of

;data being shifted in.

;

;       XBIO16          2 bytes

;       MBIO16          2 bytes

;       XIRO16          1 byte

;       HIO16           1 byte

;       FP16            1 byte

ShiftLength     EQU             8       ;Minimum 5 with standard expansion boards!

**** NOTE ****

;SPiceX mode works only with standard XPice expansion boards when

;there are more than 4 bytes of data. For less than that the

;automatic detection will sense the boards and map them as

;regular I/O.

;With custom expansion boards it may work with fewer than 5 bytes,

;depending on the design of the board. Please consult the board documentation.

****************************************************************

                SPxMode         6

                SetV            ShiftLength     ;Will henceforth control how

                                                ;many bytes get transferred

;Back here when no inputs are detected

Nothing:

                LoadX           0       ;Data

                LoadX           255     ;Bit select mask for OutputM

                OutputM         0       ;Clear result display

NextRead:

                ClrU                    ;Don't want any output data

                SPxTxfrU        0       ;Reads the inputs

                UVToMem         0,0     ;Copy V bytes from U to memory

;Identify the first non-zero byte. I is used as a counter/pointer

                LoadI           ShiftLength

ByteLoop:

                ItoX                    ;Check the counter/pointer

                Push

                GoIfZ           Nothing ;Exhausted all bytes

                DecX                    ;Bump the pointer

                XtoI

                iRecall         0       ;Get byte pointed to by I

                notm                    ;Raw input data is inverted

                GoIfZ           ByteLoop

;Fall down here if we found a NZ byte

                ItoX                    ;Byte number

                LoadX           7       ;Select 3 bits of byte number

                OutputM         3       ;Display

;Identify the first NZ bit within the byte. We know the

;byte is NZ, so there's no need to defend against not finding

;a bit set.

                iRecall         0       ;Get back the byte pointed to by I

                notm                    ;Raw input data is inverted

                LoadI           0       ;Use I as a counter

BitLoop:

                Push

                LoadX           1       ;a mask

                AndM                    ;select bit 0

                GoIfNZ          FoundBit

                IncI

                RORM

                GoTo            BitLoop

FoundBit:

                ItoX

                LoadX           7

                OutputM         0

                Blink           7

                GoTo            NextRead


Example: SPiceX and ModBus

You will find a collection of program examples on our website in the File Resources area. This includes a complete worked example of SPiceX (extended expansion) mode used in conjunction with ModBus. It includes a SPLat program SPiceX1.SPT and a companion Visual Basic 6 program in the folder SPiceX\ModBusAndSPiceX.

Both programs include extensive documentation. The VB6 program is designed to easily be adapted to other applications. It simply moves blocks of data into and out of the SPLat using ModBus.

Note: A later VB6 program is better to use if you want to grasp the basics of writing a simple ModBus handler in the PC.

For a very easy way of using a PC as a user interface to a SPLat, see SimpleHMI.


Tutorial: Thermistor temperature measurement

This tutorial is about measuring temperature using thermistors. It gives you some general theory and then goes into a detailed description of implementing actual measurements using SPLat boards. There is some maths involved, but we have reduced this to a simple procedure with the assistance of an Excel spreadsheet.

Several SPLat products include the ability to measure temperature using low cost thermistors. The scheme we have evolved uses extremely simple circuitry to produce results of modest resolution and accuracy. There are many applications where very high accuracy is not required. In other applications, the temperature range of interest is narrow. In those case, the methods presented here allow the measurement setup to be tweaked for good results, with resolutions down to 0.2°C.

Previous Topic

Next Topic

In This Chapter

Thermistors: Overview, theory

Thermistors: Using the supplied spreadsheets

Example: Simple temperature threshold with thermistor


Thermistors: Overview, theory

There are two broad types of thermistors: PTC (positive temperature coefficient) and NTC (negative temperature coefficient). For temperature measurement we use NTC thermistors. (PTC thermistors are not generally suitable for measuring temperature.)

An NTC thermistor consists of a small pellet of a special semiconductor material with two connecting wires.They can come in a number of different packages, ranging from a bare bead with two wires through to stainless steel probes or other complicated mountings.

NTC thermistors exhibit a resistance that decreases with increasing temperature.

Thermistors have a number of advantages over other electrical temperature sensors:

+ Low cost. A basic bead thermistor can be bought for well under a dollar in modest quantities.

+ High sensitivity. Thermistors produce a very large change in resistance with changing temperature. The result of this is that they can be used with very simple circuitry.

+ High accuracy. Contrary to popular belief, thermistors can be very accurate. 1°C is fairly standard, and units with 0.05°C accuracy are available for around $5 US (as a bare bead device).

+ Simple interface circuitry. Thermistors can be interfaced into a controller very simply. That is why we are able to include thermistor interfacing on our standard products at a trivial incremental cost (one resistor and one link - it costs us more to document it!). BY comparison RTDs and thermocouples require very expensive chips to interface them into a microprocessor.

Naturally, thermistors have some disadvantages. The most significant are:

- Limited temperature range. Typically, thermistors are usable only up to 150-200°C. There are, however, devices available that go as high as 750°C.

- Non-linear. Thermistors are highly non-linear. That means some arithmetic is required to produce a linear reading in degrees. Fortunately, SPLat can do the necessary calculations, so it is not so bad after all.

- No standardization. Whereas the main competing technologies, RTDs and thermocouples, are made in standardized and interchangeable types, thermistors tend to vary a lot from unit to unit and manufacturer to manufacturer. Several manufacturers do now make 'interchangeable' products, meaning units with closely defined and guaranteed characteristics.

Thermistor circuit, generalised

The diagram above shows the generalised temperature measurement circuit implemented on a number of SPLat products. Ignore, for now, the components drawn in a dashed line.

In essence, the thermistor is connected to a fixed value resistor to form a simple voltage divider, with the thermistor as the bottom leg of the divider. The divider is driven from a voltage source called Vfeed. The voltage developed across the thermistor depends on the temperature. As the temperature of the thermistor increase its resistance drops and the voltage across it decreases. This voltage is measured by an analog input on the SPLat board to produce a number. The higher the temperature the smaller the number. By performing some sums on that number we can calculate the temperature.

Just about everything in this diagram can vary, between SPLat types, between applications and between thermistor types. However, by following the instructions provided later, and using the spreadsheet we provide you, the process is quite simple.

The components drawn in dashed line exist only in certain configurations. The spreadsheet makes allowance for them.

Because the thermistor is non-linear (its resistance change from say 25°C to 30°C is not the same as its resistance change from say 75°C to 80°C), the sums we do in the SPLat program must compensate for the nonlinearity. The calculation required uses a polynomial equation. This polynomial equation is generated automatically by the spreadsheet.


Thermistors: Using the supplied spreadsheets

In January 2012 we introduced the Thermistor hash command, which makes the use of spreadsheets largely unnecessary. SPLat/PC can now do all the hard work for you.

To help you work out what you need to achieve temperature measurements with a SPLat and a thermistor, we have produced a couple of spreadsheet called thrmstr8.xls and ThermstrNorm.xls. These spreadsheets are used for determining the parameters, component values and the polynomial equation that converts raw readings into degrees. You can find the files online in the File Resources part of our website.

The main difference between the two spreadsheet files is that ThermstrNorm.xls is more recent version that uses normalized analog input readings from the fAnIN instruction. If you are using a board earlier than dialect 18, you need the other, older thrmstr8.xls version. This tutorial was however written around thrmstr8.xls, which assumes the old integer analog input instructions.

To get the most out of this tutorial please locate thrmstr8.xls and launch it now (you will need Excel 97 or a later version). You may be asked to enable macros. Without macros enabled the spreadsheet won't work.

Hint: It is an very good idea to make a copy of the spreadsheet, and work on that. Keep a copy of the original in a safe place. Working with the spreadsheet will modify it, so it's good to have an easy way to get back to the original.

Many of the spreadsheet cells have descriptive comments. Roll your mouse over the cells with small red triangles to see the comments

Previous Topic

Next Topic

In This Chapter

Thermistors: Introduction to spreadsheet

Thermistors: Setting the temperature range

Thermistors: Specifying the thermistor

Thermistors: Selecting miscellaneous parameters

Thermistors: Selecting drive voltage and resistance

Thermistors: Fine tuning

Thermistors: Evaluating the results

Thermistors: Extracting polynomial coefficients

Thermistors: Generating the SPLat code

Thermistors: A worked example


Thermistors: Introduction to spreadsheet

In January 2012 we introduced the Thermistor hash command, which makes the use of spreadsheets largely unnecessary. SPLat/PC can now do all the hard work for you.

This section will introduce you to the various parts of the spreadsheet, in a brief overview.

When you first look at the thrmstr8.xls spreadsheet it may appear rather cluttered and a bit daunting. Don't worry. If you have little of no experience with spreadsheets think of it a a form where you fill in a few squares and a whole heap of calculations are done automatically for you. To change the number in a square (cell) simple click on it and type. Do not change cells you are not specifically told to change.

You will see a number of coloured panels, some buttons, some graphs and a lot of numbers. Here are some of the main parts of the spreadsheet:

System Parameters.

These are, in the main, the numbers you need to supply. They are arranged into 4 sub-groups, which are:

  • The temperature range you need to cover
  • The specification of the thermistor you are using
  • The parameters of the SPLat board you are using
  • Two values, a voltage Vfeed and a resistance Rfeed, that need to be optimized.

All of the above are to some degree under your control.

Polynomial coefficients

These numbers are the ultimate outcome of the process. They will get coded into your SPLat program to perform the conversion from raw readings to degrees Celsius. Don't worry about the underlying math ... just have faith.

Graphs

The two graphs show the performance of the measurement system. One is the curve of raw analog input counts versus temperature. This graph is used to make Excel calculate the all-important polynomial coefficients for us. The other graph shows the resulting measurement resolution versus temperature.

Buttons

There are two buttons, [FindBestVfeed] and [FindBestRFeed]. These trigger some 'under the hood' macro code that helps you find optimum settings. The [Q] button will be introduced later.

Calculated data columns

There a quite a number of columns of calculated data. You may inspect these but you must never type into them.

The remaining bits will be explained when required.

The next sections go through the step by step procedure required to design and program a temperature measurement using thermistors and a SPLat.


Thermistors: Setting the temperature range

In January 2012 we introduced the Thermistor hash command, which makes the use of spreadsheets largely unnecessary. SPLat/PC can now do all the hard work for you.

The first step in the procedure of designing your temperature measurement system is setting the temperature range and specifying the thermistor.

It might appear that selecting the temperature range to be measured is fairly self-evident. Not necessarily! If you want a good result you need to narrow down the temperature range as far as you possibly can. The reasons for this are:

Lower limit too low

The low temperature limit is the main "driver" for the feed resistor Rfeed and/or the feed voltage Vfeed.These in turn determine the resolution of the temperature measurement. This is the most important limit to not over-specify.

Temperature range too large

The larger the temperature range the more pronounced the thermistor nonlinearity will be. This in turn means the polynomial equation (which is always an approximation!) will become less accurate, and accuracy may suffer.

The first step in the process is therefore to set the minimum and maximum temperatures.You can enter your values in the spreadsheet in the green Tmin and Tmax cells.

Don't worry if entering these numbers causes errors in the sheet. That will all get sorted out later.


Thermistors: Specifying the thermistor

The next step is selecting a thermistor. Remember, we are talking about NTC thermistors.

There are no hard and fast rules for thermistor selection, but here are some pointers.

  • Thermistors are specified in terms of R25 and Beta. R25 is the resistance at 25°C. Beta is a number that defines how much the resistance changes with temperature. Typical Beta values are around 4000. You can't do much about Beta but you can usually select an R25 value.

    Many thermistor families come in a range of R25 values.Typical values are 1K, 2K, 5K, 10K, 20K, 50K and 100K.

  • The higher the temperatures you want to measure, the higher the R25 value will need to be to get optimum performance, and visa versa. For example, if you are interested in temperatures around 0°C, a 1K or 2K thermistor will be appropriate. Around room temperature a 10K unit will work well and for boiling water look at a 100K. The rule of thumb is that the thermistor resistance at the temperature of main interest should be about the same as the feed resistor Rfeed.
  • Thermistors come in many shapes and sizes. Some are designed for temperature measurement, while others are designed for other functions like circuit protection. (A common circuit protection use is inrush current limiting in power supplies). Obviously, you want one that's intended mainly for temperature sensing or measurement. You also want one you can "live with" in terms of mounting, encapsulation etc. Some companies offer thermistors already made up into probes (click here, if you are online, to search), while others sell the bare component. Betatherm is a typical company that specializes in quality thermistors for temperature measurement, but there are many others. Temperatures.com provides many more suppliers plus a lot of additional information about temperature measurement.

We are in no way associated with any of the above referenced resources and take no responsibility for their accuracy.

  • Thermistors come in many accuracy grades. Usually they are specified in terms of the R25 tolerance, with typical specs being 0.5%, 1%, 2% or sometimes 5%. As a first approximation the temperature accuracy of the thermistor at 25°C will be half the percentage tolerance, meaning a 2% thermistor will be about 1°C accurate at 25°C. That compares very well with semiconductor temperature sensors. For example, the popular LM35 has a 2.5°C accuracy for the low grade part, and 1°C for the premium part. Betatherm and several other specialist companies have thermistors with an initial accuracy of 0.05°C at 25°C

Don't equate the thermistor accuracy with the final measurement accuracy you will achieve! There are many other sources of error that cannot be controlled as easily as just paying out a few more dollars for the thermistor. The main benefit of buying a very high accuracy thermistor is interchangeability.

Once you have selected a thermistor, look up its Beta value and R25 value and enter them into the spreadsheet.

Don't worry if entering these numbers causes errors in the sheet. That will all get sorted out later.

Once you have entered your thermistor specification, you can move on to the next section.


Thermistors: Selecting miscellaneous parameters

In January 2012 we introduced the Thermistor hash command, which makes the use of spreadsheets largely unnecessary. This page will still help you pick and set the parameters.

The next thing to do is to enter the various parameters that define your SPLat board. These are the green cells in the spreadsheet labeled "ADCSpan", "ADCInputR", "Intnl Feed Res" and "Ext Shunt Res".

The following table gives values for various SPLat products.

 | MMiXXX SL99 | SPice 10200 | AJ18 | OEM36
ADCSpan | 5 | 5 | 5 | 10
ADCinputR | 9e99 | 9e99 | 9e99 | 9000
Intnl Feed Res | 0 | 0 | 0 | See note
Ext Shunt Res | 9e99 | 9e99 | 9e99 | 9e99

Note on Intnl Feed Res and OEM36: If you use an analog output to generate Vfeed, then set this to 220 to represent the analog output resistance of the board. Otherwise set it to 0.

The Ext Shunt Res is a parameter we've provided to allow some fine tuning in special cases. Initially setting it to 9e99 (a very large number) is the same as leaving it out.

Once you have set these parameters you can move on to the next section, where we start the optimization.


Thermistors: Selecting drive voltage and resistance

We will now optimize the system.

In January 2012 we introduced the Thermistor hash command, which makes the use of spreadsheets largely unnecessary. SPLat/PC can now show you a "sandbox" tool that allows you to tweak performance.

The temperature measurement is optimized by finding the best combination of Rfeed and Vfeed. With some boards, only one or the other can be varied. With some, both can be varied. With the AJ18, both are fixed (surprisingly, even with the AJ18 we still have choices to allow the system to be optimized).

The following table summarizes the choices available for a range of products.

 | Vfeed | Rfeed
MMiXXX, SL99 | Programmable to 20V | Fixed at 10250Ohm
SPice10200 | Programmable to 20V | Selectable 100K or 9.0909K
OEM36 | Onboard fixed 10V or any external voltage | External, any value
AJ18 | Fixed at 10V | Fixed at 15K

The spreadsheet contains two buttons labeled [FindBestRFeed] and [FindBestVFeed]. Given that you have selected either a Vfeed or an Rfeed (or it's selected for you by the constraints imposed by the above table), then clicking on of the buttons will select the other value for you.

For example, with the MMi99 or SL99, Rfeed is fixed but Vfeed is programmable (there's an onboard analog output dedicated to generating that voltage).

Procedure with MMiXXX and SL99

(This is likely to apply to future boards that have thermistor temperature measurement)

  1. Set Rfeed to 10250 (simply type 10250 into the green Rfeed cell)
  2. Click the [FindBestVFeed] button. Vfeed will be calculated as the voltage that gives the best possible average resolution over the required temperature range.
  3. Note the box just above the two buttons, labeled AnOut. Because the programmable Vfeed is constrained to one of 256 possible discrete values about 80mV apart, the ideal Vfeed calculated so far cannot be achieved exactly to the millivolt. The number to the far right in the AnOut box is the nearest value that can be used. Click the [Q] button to force that value into the Vfeed box. Note the number just to the right of the AnOut label, in a pink cell. This is the value to use in an AnOut instruction to generate the Vfeed voltage.

Note that on the MMiXXX the Vfeed generator is shared by both analog inputs. That means if you are using both inputs for temperature measurements, you are constrained to having the same Vfeed value for both.

Procedure with SPice10200

The SPice10200 is an add-on for MMiXXX and SL99 that provides 3 additional temperature channels (as well as two channels of water level switching). It uses the same method as the MMi99 and SL99 controllers, including the programmable Vfeed, but allows a choice of two Rfeed values.

The SPice10200 shares the Vfeed generator with the controller board it is attached to. That means all your temperature channels will have the same Vfeed value.

The procedure for the SPice10200 is the same as above, except in step 1 you have a choice of Rfeed values of 100000 (100K) or 9090.9

Procedure with OEM36

With the OEM36 you are free to select both Vfeed and Rfeed. That means a bit more trial and error to find values that give good results and which are also convenient.

One choice is to use an analog output programmed to its maximum output voltage of 10V and then find the best Rfeed. Another possibility is to use your main power supply voltage as Vfeed (but do this only if it is regulated!)

If you have a regulated power supply, try using its value (say 24V) for Vfeed and then use the [FindBestRFeed] button to find the optimum value. When you find an Rfeed value manually replace it (type in) with the next highest available resistor value.Don't use a lower value, you will only get errors in the spreadsheet.

If you don't have a regulated power supply, try for 10V for Vfeed. Then start manually typing in various values for Rfeed and observing the results. The [FindBestRFeed] is unlikely to produce a useful result. Try Rfeed values of about the same value as the thermistor as the temperature of greatest interest. Watch the two graphs, especially the resolution graph. Resolution is the ability of the system to detect small changes (the smallest detectable change) and tends to be best where Rfeed and the thermistor resistance are the same.

Don't use the [FindBestVfeed] button with the OEM36. The button limits the voltage to a maximum of 20V, to match the characteristic of boards with programmable Vfeed generators.

Procedure with the AJ18

With the AJ18 you have no choice but to type 15000 into Rfeed and 10 into Vfeed. (By way of explanation, the AJ18 was designed to control water temperature to 85°C with a particular thermistor, so its pretty much optimized for that.). However, you still actually have a lot of control over performance via thermistor selection and by using an external shunt resistor.


Thermistors: Fine tuning

The primary objective of this whole exercise is to get the best possible resolution out of the temperature measurement.

The procedure so far has helped establish the optimum values of Vfeed and Rfeed, in order to get the best resolution. There is one more avenue for possible improvement, and that is adding a shunt resistor across the thermistor. Normally this will simply reduce the overall performance. If you experiment with a few values (try 10000 to 100000) you will find it tends to "roll" the resolution curve to the right. It frequently also lifts the whole curve.

Nevertheless, it is still worth experimenting with, particularly with the AJ18 and the OEM36 using the main power supply (eg 24V) as Vfeed.


Thermistors: Evaluating the results

Now is the time to start evaluating how good your results are. The spreadsheet contains a number of features to help you with this.

The most important measure of your design is the resolution. The resolution is the smallest change in temperature the measurement system is able to detect. The resolution graph plots this as a function of temperature.

A typical best case figure for resolution is 0.2°C. Usually the best resolution (low point on the resolution curve) will occur at or near the low end of the temperature range, though invoking the shunt resistor may move the low point to the right. You need to evaluate the resolution predicted by the spreadsheet and decide if it is adequate for your application. If you are not happy with the results you may still be able to get an improvement by trying a different thermistor and repeating the process.

Accuracy is a different matter, and much harder to evaluate. If you look carefully at the Temperature versus Count graph, you will see it consists of two curves. One, drawn with a thick line, is the real raw AnIn reading versus temperature curve. The other, drawn in a thin line, is a "trendline" that Excel has calculated to try and match the real. This trendline represents the outcome of the calculations your SPLat program is going to perform on the raw AnIn readings, and is represented by the polynomial equation displayed at the top of the graph. As long as the two curves lie on top of each other, all is well and the polynomial equation will contribute negligible errors to your measurements.

Try this experiment: Add 200 to Tmax. This will force the polynomial to cover a much larger range of values and you will see it no longer tracks the 'real' curve. Remember to reset Tmax!

The polynomial is only one source of errors. Other sources are:

Thermistor accuracy. This was discussed earlier. The best reason for buying premium accuracy thermistors is that they are interchangeable. That means you can calibrate your system with one thermistor, and know it will work to a very close tolerance with any other thermistor of the same type.

Analog measurement errors. The SPLat analog inputs have a finite accuracy specification. This varies from board to board, but is typically in the 1% to 2.5% of full scale (2 to 6 counts). That means the possible error from this source is 2 to 6 times the resolution (remember, resolution is the temperature change represented by a 1-count change in the raw analog reading).

Resistor errors. The Rfeed and optional external shunt resistors contribute potential errors.

Vfeed errors. If you are using an onboard source of Vfeed, the error contribution of Vfeed will be minimal because the analog output used to generate Vfeed uses the same reference voltage as the analog input measurement, and any changes tend to cancel (this is called ratiometric measurement). If you use an external Vfeed (i.e. with the OEM36) then there is no longer any cancellation. That is why we suggest you use an external Vfeed only if it is from a regulated power supply.

Thermistor self-heating. The spreadsheet contains a column headed "mW" to allow you to evaluate this.

What to do about the errors?

If your application is non-critical, say heating the water in a commercial dish washer, then simply ignore the errors. You will get to within 2-3°C without calibration, so don't worry about it.

In most applications there is only a certain temperature, or a narrow range of temperatures, that are of interest, and accuracy is important. For example, a fermentation vessel may require a controlled temperature of 33°C±1°C, so that's the temperature that really counts. The trick then is to make a calibration adjustment to the reading at or close to that particular temperature. This can be done via an onscreen menu, using shadow memory or permanent memory to store the calibration offset. A simpler, but less elegant way is to simply add in a calibration offset determined before your program is downloaded to the SPLat board. Naturally, every system you make will need to be individually calibrated, if you need high accuracy.

If you are using a low accuracy thermistor, you will need to subject it to a precisely known temperature, than adjust the system to get the right reading.

If you are using high accuracy, interchangeable thermistors, then you can use a fixed resistor equal to the thermistor resistance at the temperature of interest. That saves having to generate a known temperature.


Thermistors: Extracting polynomial coefficients

In January 2012 we introduced the Thermistor hash command, which makes the use of spreadsheets largely unnecessary. SPLat/PC can now do all the hard work for you.

Once you have established the optimum operating parameters, it is time to extract the polynomial coefficients and generate the SPLat code.

The equation at the top of the Temperature versus Count graph contains the polynomial coefficients. If you click on the equation it will be revealed to be a text box. You can use your mouse to highlight any part of the box and copy it (CTRL+C). (If you accidentally change the equation, use CTRL+Z to undo the change).

If you very carefully copy the coefficients from the equation into the Polynomial Coefficients box (yellow cells), you will be able to get a table of polynomial errors versus temperature. The comments in the spreadsheet detail this.

Caution: It is very easy to miss a minus sign in the equation where it linewraps. If you get huge errors after you have copied the coefficients, double check the copied values.


Thermistors: Generating the SPLat code

In January 2012 we introduced the Thermistor hash command, which generates the code. Let SPLat/PC do all the hard work for you!

Coding the SPLat program to make the conversion is easy ... we've done it for you. The following is the conversion code, with a particular set of polynomial coefficients. What we have done is copy the whole equation as a comment, removed spaces between +/- signs and the numbers, and then manually copied and pasted the coefficients into the code. One floating point location (4-bytes of storage) is required to hold the intermediate and final result.

;y = -1.124166E-10x5 +1.014140E-07x4 -3.985487E-05x3
;    +8.462339E-03x2 -1.219136E+00x +1.133853E+02
;Come here with a raw analog input value in X
      Float
      fStore       Temperature
      fLoadQ       -1.124166E-10      ;<< X^5 coefficient
      fMul
      fLoadQ       +1.014140E-07      ;<< X^4 coefficient
      fAdd
      fRecallQ     Temperature
      fMul
      fLoadQ       -3.985487E-05      ;<< X^3 coefficient
      fAdd
      fRecallQ     Temperature
      fMul
      fLoadQ       +8.462339E-03      ;<< X^2 coefficient
      fAdd
      fRecallQ     Temperature
      fMul
      fLoadQ       -1.219136E+00      ;<< X^1 coefficient
      fAdd
      fRecallQ     Temperature
      fMul
      fLoadQ       +1.133853E+02      ;<< X^0 coefficient
      fAdd
      fStore       Temperature        ;Final result

The only other thing you need to do is to ensure that the Vfeed voltage is initialized, if you are using a programmed voltage. The spreadsheet gives you the value to output to the analog output port in the case of MMi99 or SL99 (the AnOut value in the pink cell). For OEM36 you would always put out 255.


Thermistors: A worked example

The following example summarizes the procedure for a SPice10200.

A SPice10200 is being used in conjunction with an MMi99 to control a wine fermenter. The ideal fermentation temperature is 33°C. Available thermistors are 2K, 5K, 10K and 20K, all with Beta=3920. Find the best possible design to get best resolution at 33°C.

The SPice10200 gives a choice of Rfeed of 100K and 9.09K. All the available thermistors will be less than 20K at the temperature of interest because it is above 25°C and the thermistor resistance drops with heat. Remember, best resolution occurs about where Rfeed and the thermistors resistances are equal. Hence we intuitively expect a 20K thermistor and 9.09K Vfeed to be best.

Set up the spreadsheet with all the preliminaries as shown below.

Tmin | 15
Tmax | 50
R25 | 20000
Beta | 3920
ADC Span | 5
ADC input R | 9.00E+99
Intnl feed Res | 0
Ext. Shunt resistance | 9.000E+99
Rfeed | 9090
Vfeed | 6.4393

The last value, Vfeed, is the result of clicking [FindBestVFeed]

The Tmin value has been selected so room temperatures will give a sensible reading, when the vessel is not in use. What effect does that have on our resolution where it counts, at 33°C?

With the above settings we get a resolution at 33°C of 0.305°C.

If we change Tmin to 28°C, and click [FindBestVFeed] again, we get 0.26°C resolution at 33°C. If we shoot for Tmin=0 we get about 0.35°C resolution. In this case Tmin is clearly not making very much difference.

If we try the 10K thermistor we get a slightly better resolution of 0.3°C at 33°C with Tmin=0°C. The 5K thermistor gives a very similar result, but with better resolution down at 0°C and worse above 33°C. Overall the 10K unit is the best choice, so our intuition was a bit off target!

Now here comes the surprise: The 2K thermistor gives 0.2°C resolution at 33°C, though to get there we have to lift Tmin to 16°C.


Example: Simple temperature threshold with thermistor

There is now a very easy way to calculate temperatures from thermistor readings.

There may be times when all you need out of a thermistor is a simple yes/no as to whether the temperature is over or under a fixed limit, or threshold.

There is a collection of program examples our website in the File Resources area. In there the folder thermistor contains a SPLat example, SIMPLE1.SPT, that does just that. It won;t give you a temperature value but will resolve if the temperature is below or above the threshold.. The process for incorporating this function into your own program is this.:

  1. Use a spreadsheet to establish the thermistor operating conditions.
  2. Copy the code from our example program into your program
  3. Extract two constants out of the spreadsheet and edit them into our example program (expanded below)
  4. Insert GoSubs into your program to initialize the thermistor circuit and to test the temperature.

SIMPLE1.SPT contains instructions on integrating itself into your program. Below is the procedure for establishing the two required constants (which depend on the thermistor you are using and on the temperature you are interested in.

  1. Select a thermistor. Generally this works best with most SPLat boards if the thermistor has a resistance of about 10K around the temperature of interest. R25 is one of the two key thermistor parameters. The table below gives suggested R25 values to aim for versus threshold temperatures.
  2. Establish the actual R25 value for your selected thermistor, and its Beta value (sometimes called B, and usually a number around 4000).
  3. Run Excel and load the spreadsheet Thermstr8.xls
  4. Enter R25 and Beta into the green cells labeled R25 and Beta
  5. Set Tmin and Tmax values. I suggest Tmin about 10°C below your threshold temperature and Tmax 10°C above. If you think you may want to change the threshold temperature, use values that bracket the range you expect to need. Note the spreadsheet is all in Celsius, and you must not try and use Fahrenheit values!
  6. Hover the mouse over the other parameter labels and read the pop-up boxes. Follow the instructions given to enter the remaining values.
  7. Click the "Find best Vfeed" button.
  8. Click the "Q" button
  9. Read off the AnOut value in the hot pink cell. This is the value for cTLM_Drive in the program.
  10. Find the required threshold temperature in the column labeled "T".
  11. Read off the corresponding number from the "Count" column. Round it to the nearest integer value. This is the value for cTHM_ThresholdCount in the program.
  12. Edit the program at the places flagged with <<<<<<<<<.

Suggested thermistor R25 values versus threshold temperature

If you can select a thermistor close to the R25 listed below, you will get the best temperature discrimination. This is not of cosmic importance, assuming you don't need huge accuracy. The "Resolution" column in the spreadsheet is indicative of performance for a given set of parameters. The smaller the number the better.

Temp °C | R25
-35 | 625
-20 | 1250
-5 | 2500
10 | 5000
25 | 10000
40 | 20000
55 | 40000
70 | 80000
85 | 160000
100 | 320000
115 | 640000


Tutorial: Multitasking (Superseded)

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.

One of the aspects of control programming that causes the most grief and confusion is how to create a multitasking system. Multitasking is the ability of a program to seemingly do two or more things at once. For example, an industrial parts washer may need to fill up with water at the same time as it is controlling a heater, dispensing detergent and monitoring operator push buttons.

What you will gain from this tutorial (learning outcomes)

This tutorial will introduce you to simple techniques for creating multitasking programs using SPLat. It will introduce the concepts one at a time, working through and building up an example application. The example is a bit contrived, simply because it attempts to illustrate the ideas with as few lines of code as possible. Think of it, if you like, as a skeleton on which you can build you own application.

What you need to know first (Prerequisites)

You will gain the most benefit from this tutorial if you first do the online FastTrack training course. That course introduces you to the fundamental concepts of Finite State Machines, masquerading as "SPLatMaps". It also introduces you to SPLatty, our (somewhat) anthropomorphized alter-ego for the SPLat processor.

Please note: The interactive/online training course is very old. Everything in there is valid and worth learning, especially the underlying principles. However the course pre-dates significant developments, most notably our MultiTrack multitasking technology which makes SPLat programming even easier. So, if you are relatively new to controller programming, the course will give you a good foundation, but please don't think it represents the full SPLat story.

You will also need to have a basic understanding of:

  • SPLat registers and timers. That is covered in the "classic" tutorial that is part of the SPLat/PC help file.
  • Subroutines.

In summary, this tutorial is an advanced topic that assumes you have mastered the nuts and bolts of SPLat programming.

Note: Don't let the informal language in this tutorial fool you. The "information density" is very high. The concepts are introduced rapidly. Make sure you have completely taken on board and understood each idea, each example and each page before you move on. I do not repeat myself! If you do maintain focus you will be rewarded by learning a heap of advanced material very quickly.

Previous Topic

Next Topic

In This Chapter

Multitasking: Basic ideas

Multitasking: The main loop

Multitasking: A simple yielding subroutine

Multitasking: Now, where was I?

Multitasking: A first complete program

Multitasking: Properties and Data Hiding

Multitasking: A closer look at Suspend and Resume

Multitasking: Additional notes on initialization.

Multitasking: Branch and Target

Multitasking: Advanced topics


Multitasking: Basic ideas

Multitasking is an illusion!

Imagine this: The local chess club has somehow persuaded the grand master Boris Checkovski to visit. Checkovski agrees to play all 15 club members ... simultaneously. 15 tables are set up with chess boards, one for each member. The members sit down at their respective boards. Checkovski moves from table to table, pausing but a few seconds at each table to make a move, before proceeding to the next table. An hour later 15 defeated club members sit there, deep in thought, wondering what ever hit them, while Checkovski is happily tucking into the vodka and caviar the club so generously laid on for him out of your membership fees.

To each of the club members, Checkovski played a brilliant game of chess. To a member of the audience he was moving rapidly around the room, playing now a bit of one game, now a bit of another game. As he moved to each new table he somehow managed to figure out what was going on with that particular game and resume from where he left off, taking into account the changed circumstances. He made his move, then suspended himself from that game and moved on to resume the next.

Was he playing 15 games at once? No! He was playing a tiny bit of each game at a time. While he was at Harry's table he was totally focused on Harry's game. Then when he went on to Mary's table he picked up the thread of that game (including any move Mary may have made since last time), evaluated the situation and made his next move. Only by advancing rapidly from one table to the next did he create the illusion of playing 15 games at once.

And so it is with SPLat. Remember SPLatty from our online training course? SPLatty (a.k.a. the SPLat processor) can only do one thing at a time, but he can move very rapidly from task to task, creating the illusion of performing several tasks at once. All that is needed for this to happen is a mechanism to help SPLatty remember where he was in each task when he comes back to resume work on that task.

What is really nice about the techniques you are about to learn is that they provide a boringly consistent way of creating multitasking programs. Boring is good. Boring means fewer nasty surprises. Consistent is good. Consistent means the very same framework you use in program A will also work in program B. That means you will spend less time on the "multi" and more on the "task", and with practice you will get better and better at it.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: The main loop

Our multitasking scheme uses one of the simplest possible program structures (short of ad hoc spaghetti code, which has no structure at all!). We call it "A main loop calling individual tasks as subroutines". If that sounds like an indigestible bowl of something other than spaghetti, fear not, all shall become clear!

Consider the following program fragment:

MainLoop
         GoSub     FlashTheLight
         GoSub     OnOffSwitch
         GoSub     RingTheBell
         GoTo      MainLoop

This code loop will call each of the 3 subroutines in turn, and repeatedly, in quick succession. Well, it will be in quick succession providing none of the subroutines keeps SPLatty tied up indefinitely or even for an undue amount of time.

That proviso is the crux of the matter. If we can structure each subroutine so it never, ever, ever monopolizes the processor (SPLatty), then we will have created an illusion of 3 things going on at once. That means:

  • No WaitOn/WaitOff type instructions
  • No Pause instructions
  • No "tight" loops waiting for something to happen.

That means some aspects of FastTrack programming can no longer be used. FastTrack, after all, is there to make it easy for newbies to get useful results quickly. Now it's time to get rid of the trainer wheels and move on. In the next section we'll start looking at ways and means of building subroutines that are free of such instructions and structures.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: A simple yielding subroutine

I am going to use the term "yielding" to describe a subroutine that is made in such a way that it "yields" control of the processor, i.e. frees up SPLatty, as soon as it has nothing useful to do. In our multitasking scheme, every subroutine must be yielding. Let's illustrate this with a very simple lamp flasher.

First, here is a program that will flash an output "FlashLight" 500mS on, 500mS off forever, without yielding

FlashTheLight
        Test      0              ;Test the timer
        GoIfT     FlashTheLight  ;go if still timing
        SetTimer  0,5            ;Restart the timer
        InputO    FlashLight     ;Read back the output
        Not                      ;Reverse the value
        Output    FlashLight     ;Update the output
        GoTo      FlashTheLight  ;Repeat forever

The program uses timer 0. It starts by testing the timer. If it is still timing, it just jumps back and tests again. Once it discovers the timer has timed out, it reads back the output, inverts it (NOT) and sends the inverted value back to the output (hence toggling the output). It then jumps back to the start and repeats the whole process.

I've deliberately not used a Pause instruction, because Pause is verboten in our simple multitasking scheme, and that's where we are heading.

The above program will make a fine light flasher but that's it, that's all you will get. What a shame, using up 100% of SPLatty's capability on something that really should only use up maybe 0.1%!

The following code is a subroutine that will toggle the state of FlashLight every 500mS, but yields as quickly as it can each time it is called.

FlashTheLight
        Test      0              ;Test the timer
        RetIfT                   ;Return (yield) if still timing
        SetTimer  0,5            ;Restart the timer
        InputO    FlashLight     ;Read back the output
        Not                      ;Reverse the value
        Output    FlashLight     ;Update the output
        Return                   ;Yield

You will notice all I've done is replace the GoTo's with Returns (it's not always that simple, but it's also not much more complicated!). Providing this subroutine is called frequently from a main loop, it will merrily flash the light while taking up very little of SPLatty's time.

I suggest you stop here and make an actual program, complete with a main loop, that flashes a light using this technique. You can get the main loop from the previous section. Run it in SPLat/PC and make yourself quite comfortable with how it works.

In the next section I will introduce some nifty instructions that let SPLatty "bookmark" where he is in a program, so he can go off and do other things and still get back to where he left off.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: Now, where was I?

Having done the Online (FastTrack) training course (you have done it, haven't you?) where you first met SPLatty, you will be very familiar with SPLatMaps. SPLatMaps are our particular take on Finite State Machines (FSM). FSMs are a widely recognised method of designing sequential programs that can respond to external conditions in ways that vary depending on "where they are".

push on push off light splatmap

The very first example in the Online course was a push on - push off light switch. The SPLatMap for that function is reproduced here. You should recognise it! The circles represent times in the life of SPLatty where he is waiting for something of interest to happen. We call these states. The arrows leading out of the circles, with attendant text labels, represent external events that SPLatty is interested in at that time (in that state). The rectangles represent actions SPLatty takes when the event occurs. Finally the arrows out of the action boxes tell us what new state SPLatty goes to when he has taken the indicated action. If this is all new to you NOW is the time to go and do the Online training course.

FastTrack (non-multitasking) code for this function is (assume Switch and Light are defined as an input and an output respectively):

OnOffSwitch
State0
            GoIfInOff   Switch,State0
            On          Light
State1
            GoIfInOn    Switch,State1
State2
            GoIfInOff   Switch,State2
            Off         Light
State3
            GoIfInOn    Switch,State3
            GoTo        State0

This will look different to the code in the online course, because I've taken out the now verboten instructions WaitOn and WaitOff. Study the above code until you are totally comfortable with it.

The above program is still no good for multitasking, as it will totally tie up SPLatty watching the button. There is still something missing.

What we need is a mechanism that will allow us to do a subroutine return each time we test the switch and find it is not in the condition (on or off) we are interested in. The problem is, we can't just put in a Return instruction, because next time we call the subroutine OnOffSwitch we will start the subroutine at the top. What we need is something that allows SPLatty, each time he gets sent into this subroutine, to resume working from where he left off last time.

From dialect 12 we have such a mechanism (there has been one in SPLat from Day One, but this one is conceptually easier to cope with). The mechanism is a pair of instructions called Suspend and Resume.

What the Suspend instruction does is to make SPLatty "bookmark" where he is in the program, then do a subroutine return.

What the Resume instruction does is to make SPLatty retrieve the "bookmark" and resume program execution from that point, which is where he left off with the Suspend.

The "bookmark" is actually a program memory address that is captured and stored in RAM memory. It takes 2 bytes to store such an address, so you need to set aside 2 bytes for the purpose.

Here is the OnOffSwitch subroutine re-written with Suspend and Resume instructions. Each of those refers to memory location OnOffAddr, which must have been defined in an mEQU directive and must have had 2 bytes reserved for it.

OnOffSwitch
            Resume      OnOffAddr
OnOffInit
State0
            Suspend     OnOffAddr
            GoIfInOff   Switch,State0
            On          Light
State1
            Suspend     OnOffAddr
            GoIfInOn    Switch,State1
State2
            Suspend     OnOffAddr
            GoIfInOff   Switch,State2
            Off         Light
State3
            Suspend     OnOffAddr
            GoIfInOn    Switch,State3
            GoTo        State0

You will see that in this code a Suspend has been added inside each wait loop. At the "subroutine entry point", i.e. OnOffSwitch, the line the MainLoop does a Gosub to, we have a Resume. That will grab the "bookmarked" address and then jump to that spot in the program.

The final detail is the label OnOffInit. This is the initialization entry point. It must be called before any call is made to the "main entry point" OnOffSwitch. It serves to "prime" the bookmark ready for the first call to the main entry point, OnOffSwitch.

Another little point: OnOffInit and State0 actually both refer to the same line of executable code (remember a line label my be on a line of its own). I chose to apply two different labels because they will be used in two different contexts, it helps to make the program more readable and it aids me in being boringly consistent in how I label lines. Later on I will introduce you to the full and actual labelling scheme I use.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: A first complete program

Let's now put all this together into a complete program. The following code contains all the housekeeping stuff. Also, I have changed some labels into the form I prefer to use. This is a consistent way of generating label names that in particular saves a lot of head scratching to come up with names and also makes it much easier to find what I want in a 6000 line program. The basis of my labeling scheme is that I give each function (subroutine) a short 2 or 3 letter capitalised prefix.

*****************************************
; I/O assignments
Light      oEQU      15            ;MMi99 front panel LED
FlashLight oEQU      14            ;MMi99 front panel LED
Switch     iEQU      8             ;MMi99 front panel push button
*****************************************
; RAM assignments
OOS_Address mEQU     0             ;2 bytes for a suspend address.
*****************************************
; initialize code
         GoSub       FTL_Init
         GoSub       OOS_Init
*****************************************
; Main loop
MainLoop
         GoSub       FTL_Update    ;Update the flashing light
         GoSub       OOS_Update    ;Update the push on/push off light
         GoTo        MainLoop
*****************************************
;On-off switch function
;Short name: OOS_
;Function: Toggles the Light output each time the front panel button
;          switch is pressed.
;Methods:  OOS_Init
;          OOS_Update

OOS_Update

         Resume      OOS_Address   ;Resume from where we last Suspended

OOS_Init

OOS_0

         Suspend     OOS_Address

         GoIfInOff   Switch,OOS_0

         On          Light

OOS_1

         Suspend     OOS_Address

         GoIfInOn    Switch,OOS_1

OOS_2

         Suspend     OOS_Address

         GoIfInOff   Switch,OOS_2

         Off         Light

OOS_3

         Suspend     OOS_Address

         GoIfInOn    Switch,OOS_3

         GoTo        OOS_0

******************************************

;Light flasher function

;Short name: FTL

;Function: Flash output FlashLight at 1Hz, 50% duty cycle.

;Methods:  FTL_Update

;          FTL_Init

FTL_Update

         Test        0              ;Test the timer

         RetIfT                     ;Return (yield) if still timing

         SetTimer    0,5            ;Restart the timer

         InputO      FlashLight     ;Read back the output

         Not                        ;Reverse the value

         Output      FlashLight     ;Update the output

FTL_Init

         Return                     ;Yield

Copy and paste this program into SPLat/PC now (you may have to manually fix up some weird stuff inserted by the copy and paste). You will need SPLat/PC V6.12.0 or later set to dialect 12 or later. Run the program in simulation mode. You should be able to actually observe that the program passes through different parts of OOS_ as you click the button (input 8) on and off. If you look at RAM locations 0 and 1 (in the Data Memory window) you will even be able to see the current line number where OOS_ has suspended from. The line number is 256*RAM(0) + RAM(1). You should even be able to relate that back to the actual program line number displayed in the SPLat/PC editor status bar. Note that in the simulator the editor line number is saved in RAM. In the actual board, it is the program memory address that is stored, which is a totally different number.

Can you see how each of the two subroutines is operating quite independently of the other, and how all the main program loop does is to call them in quick succession? Can you see how easy it would be to add a third, and a fourth subroutine (function)? Can you imagine a large control program taking care of a quite complex piece of equipment, and structured as maybe 10 or 20 or 30 such subroutines, each controlling a separate aspect of the equipment? In the next section I will evolve this into the idea of a task or process, and start to show you how a program can indeed be structured as a number of such tasks that perform their individual functions while communicating with each other to co-ordinate their efforts.

"Why", I hear you ask, "have you got FTL_Init when it does nothing?" (You did wonder, didn't you?). The reason is to make the programming as boringly consistent as possible. Doing things in consistent way throughout can save a lot of work in the long run, even if it means a bit more typing in the short run.

In the next section we will establish some communication between our two tasks, to show how you can co-ordinate activities

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: Properties and Data Hiding

By now you should have a program running in SPLat/PC that is merrily flashing a light and at the same time, and quite independently, toggling another light on and off each time you press a button. You will have (should have!) studied how the Suspend/Resume mechanism works, and be able to see how such a simple idea can allow 2, 3 or more tasks to run quite independently and asynchronously.

But what if we need to have the tasks interact? Suppose we want to make it so the flashing light is only active while the other light is off?

There are many ways of achieving this, some better than others. The way I will show you is a "good" way, trust me!

Firstly, I will obviously need to re-write the flasher task to take into account the state of the on/off light. In fact, it will now become a full-blown SPLatMap based state machine, with Suspend/Resume instructions.

Multitasking flasher splatmap

The SPLatMap shows the logic of the revised flasher. It starts in state 0, the initialize state. I have called the flashing light "flash" and the other one "lamp", to try and avoid confusion in the diagram. Assuming the flasher task can somehow discover the condition of the lamp, it will flash its output only while the lamp is off.

So how does the flasher find out about the condition of the lamp? It asks the on/off lamp switch task (OOS_)!

What I will do is create a "Property" (attribute) of the OOS_ task that tells me if the lamp is on (true) or off (false). I will call this property OOS_LampOn. The flasher (or any other task in my program) can get OOS_LampOn property by calling the "property get" subroutine OOS_GET_LampOn. It will then be the responsibility of the OOS_ task to return the correct value in the X register.

How do I implement the property and the property get? There are several ways. What is really interesting is that it doesn't matter to the flasher how it is done, just as long as a call to OOS_GET_LampOn returns the correct value. The method I will use is to have OOS_ maintain a semaphore that tracks the condition of the lamp. All OOS_GET_LampOn then has to do is grab the semaphore and return it.

In the new program below you will notice the following changes (I have flagged them with a row of <<<) compared to the previous version:

  1. I have reserved the first 8 bytes of RAM for semaphores. This is in accordance with our simplified semaphore strategy.
  2. There is a semaphore called OOS_Lamp, which is owned and maintained by the OOS_ task.
  3. I've (somewhat pedantically - read "boringly consistent") added "real" initialization code to OOS_
  4. OOS_ has been modified so it updates the semaphore to match the state of the lamp.
  5. The new OOS_GET_LampOn property get entry point returns the state of the semaphore in X
  6. The flasher, FTL_, has been completely re-written to follow the SPLatMap above.

Here is the complete program.

*****************************************
; I/O assignments
Light      oEQU      15            ;MMi99 front panel LED
FlashLight oEQU      14            ;MMi99 front panel LED
Switch     iEQU      8             ;MMi99 front panel push button
*****************************************
; RAM assignments
* Reserve the first 8 bytes for 64 semaphores<<<<<<<<<<<<<<<<<<<<<
; Semaphores
OOS_Lamp   sEQU     0             ;Light is on   <<<<<<<<<<<<<<<<<<<<<
*****************************************

OOS_Address mEQU     20            ;2 bytes for a suspend address.

FTL_Address mEQU     22            ;2 bytes for a suspend address.

*****************************************

; initialize code

         GoSub       FTL_Init

         GoSub       OOS_Init      ;Absolutely essential!!!!!!

*****************************************

; Main loop

MainLoop

         GoSub       FTL_Update    ;Update the flashing light

         GoSub       OOS_Update    ;Update the push on/push off light

         GoTo        MainLoop

*****************************************

;On-off switch function

;Short name: OOS_

;Function: Toggles the Lamp output each time the front panel button

;          switch is pressed.

;Methods:  OOS_Init

;          OOS_Update

;Properties:                     ; <<<<<<<<<<<<<<<<<<<<<

;          OOS_GET_LampOn    Boolean in X, true if lamp is on

OOS_GET_LampOn                     ;<<<<<<<<<<<<<<<<<<<<<

         RecallS     OOS_Lamp

         Return

OOS_Init                           ;<<<<<<<<<<<<<<<<<<<<<

         Off         Light

         ClrS        OOS_Lamp

         GoTo        OOS_0

OOS_Update

         Resume      OOS_Address   ;Resume from where we last Suspended

OOS_0

         Suspend     OOS_Address

         GoIfInOff   Switch,OOS_0

         On          Light

         SetS        OOS_Lamp      ;<<<<<<<<<<<<<<<<<<<<<

OOS_1

         Suspend     OOS_Address

         GoIfInOn    Switch,OOS_1

OOS_2

         Suspend     OOS_Address

         GoIfInOff   Switch,OOS_2

         Off         Light

         ClrS        OOS_Lamp      ;<<<<<<<<<<<<<<<<<<<<<

OOS_3

         Suspend     OOS_Address

         GoIfInOn    Switch,OOS_3

         GoTo        OOS_0

******************************************

;Light flasher function Totally rewritten <<<<<<<<<<<<<<<<<<<<<

;Short name: FTL

;Function: Flash output FlashLight at 1Hz, 50% duty cycle.

;Methods:  FTL_Update

;          FTL_Init

FTL_Update

         Resume      FTL_Address

FTL_Init

FTL_Set0

         Off         FlashLight

FTL_0

         Suspend     FTL_Address

         GoSub       OOS_GET_LampOn

         GoIfT       FTL_0

FTL_Set1

         On          FlashLight

         SetTimer    0,5

FTL_1

         Suspend     FTL_Address

         GoSub       OOS_GET_LampOn

         GoIfT       FTL_Set0

         Test        0

         GoIfT       FTL_1

FTL_Set2

         Off          FlashLight

         SetTimer    0,5

FTL_2

         Suspend     FTL_Address

         GoSub       OOS_GET_LampOn

         GoIfT       FTL_Set0

         Test        0

         GoIfT       FTL_2

         GoTo        FTL_Set1

Copy and paste this program into SPLat/PC, and get it going (you may have to manually fix up some weird stuff inserted by the copy and paste).

Once you have fully absorbed how this works, here's an exercise for you: Change the way OOS_GET_LampOn works. Rewrite it to use an InputO instruction instead of the semaphore. Does this affect how FTL_ uses the OOS_LampOn property?

What you have just seen with that exercise is the principle of Data Hiding (or encapsulation ... the two terms tend to get used interchangeably though strictly speaking that is incorrect.). What that means is that how OOS_ keeps track of its own internal stuff is its own business and no-one else's. As long as OOS_ coughs up the right answer when asked for a property, it doesn't matter to the caller how it happens. The other side of this is that no-one outside OOS_ is allowed to directly access data or resources that belong to OOS_. This principle is carried further. In terms of coding SPLat, if you stick to the rule that all exchanges with a task must be via GoSubs, you will stay on the straight and narrow. We call the entry points Methods if they result in an action, and Property GETs and SETs if they involve GET-ing or SET-ing of data values. Where-ever possible any data passed during these calls should be held in registers like X or W, not in RAM.

You can consider the Methods and Properties of a task as its interface to the world. Think of the task as a black box. From outside the box you do not know, or care, what goes on inside. All you want to know is that if you poke and prod it according to the interface specification, it will do the things it is meant to do. If for some reason you have to modify the internals of the box, then as long as you don't alter its interface you can be confident that the rest of your program will be unaffected.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: A closer look at Suspend and Resume

In this section I will discuss a few aspects of the Suspend/Resume mechanism.

Multitasking: Suspend/resume concept

The above diagram illustrates the Suspend/Resume mechanism.

The Resume instructions retrieves a previously saved program location from RAM, and resumes the program from that point. That means that:

The resume address must be "primed" by initialization code.

The Suspend instruction saves the current location and then executes a Return. That means that:

The Suspend must be executed inside a subroutine.

Suppose that inside the subroutine where you do the Resume, you call another subroutine. Could you do a Suspend from within that "inner" subroutine? Well yes, you could, providing the inner subroutine has a separate designated pair of RAM bytes to store its suspend address. If it doesn't, then bad things will happen.

Another important thing to note is that there is no automatic mechanism for saving what's in the registers (X, Y, W, Q etc) between calls into Task1.

You should always assume that doing a Suspend will result in every register being altered.

The following diagrams illustrate a few of the possible permutations of subroutines and suspend/resume. In all these examples the resume address is assumed to have been initialized.

The first diagram (below) shows a 2nd level subroutine call and return from inside a first level subroutine that has a suspend/resume mechanism. That is perfectly OK, because the suspend and resume are at the same subroutine nesting level.

Multitasking: suspend resume concept 2 a

In the next diagram below the second level subroutine also has its own suspend/resume. As long as it uses a separate place in RAM to save its resume address, that's fine (mind you, I've never had to resort to a structure that involved). This case was also mentioned above.

Multitasking: suspend resume concept 2 a

In the next example, we GoSub a level 1 subroutine which subsequently does a Return (rather than a Suspend). What will happen? It will return to the main program as expected. The next time it is called, it will simply resume from exactly the same place as the first time, as the resume address in RAM is unchanged.

The next case is a possible trap for young players. In the level 1 subroutine we resume, then call a level 2 subroutine and do the suspend from there. That suspend will return SPLatty to the level 1 subroutine, not to the main program.

Why are Suspend and Resume not symmetrical?

You may be wondering why Suspend performs a subroutine return and yet Resume does not perform a subroutine call. Surely it would be more logical to make them behave symmetrically? The reason they are not symmetrical is this: By making it so Resume must be executed inside a subroutine, it gives you the opportunity to place any code that is common to every state of the subroutine (task) before the Resume. This can save a lot of code. The most common instance is a task that is always synchronized to either a time event or an InputK.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: Additional notes on initialization.

I believe it is good practice for every task to have its own entry point (subroutine call address) for initialization. It's all part of being boringly consistent!

However, if you prefer, you can avoid having to write explicit initialization by exploiting the following special feature of the Resume instruction: If the resume address is zero, indicating it hasn't been initialized, the program will "fall through" to the instruction following the Resume. So I fibbed earlier about initialization being essential! I do really think it is good practice to have an explicit initialization call for all tasks.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: Branch and Target

The Suspend and Resume instructions came into being in SPLat dialect 12. There has been another mechanism available since the very first SPLat ever made. This is the instruction pair Branch and Target. There are also alternatives to Branch called BranchM, which "branches on" memory rather than on X and BranchR, which "branches on" R rather than on X.

The best way to describe how Branch works is with the following code fragment (read the comments!):

        BranchM    StateNumber
        Target     State0   ;Go to the line State0 if StateNumber contains 0
        Target     State1   ;Go to the line State1 if StateNumber contains 1
        Target     State2   ;Go to the line State2 if StateNumber contains 2
        Target     State3   ;Go to the line State3 if StateNumber contains 3
        .... etc .....

So, the BranchM instruction grabs the number in the specified RAM location (here StateNumber) then counts its way down the Target list to find the n'th entry (starting counting at 0, not 1). The target list must follow immediately after the BranchM instruction. If the argument (in this case StateNumber) points past the end of the list, SPLat/PC will raise an error.

If you are familiar with the early Basic languages, you will recognise this as something similar to ON..GOTO. With more modern languages it is like SWITCH or CASE but with the selector values constrained to being consecutive integers.

How do we use this in multitasking? Well, the idea is that StateNumber contains the, ehr, state number, and each target address be for the code that handles that state.

A few screens ago I showed the OnOffSwitch subroutine using Suspend and Resume. Here is the same routine using Branch and Target. This assumes a single byte of RAM is reserved with the name OnOffState.

OnOffSwitch
            BranchM     OnOffState
            Target      State0
            Target      State1
            Target      State2
            Target      State3
OnOffInit
Set0
            SetMem      OnOffState,0
            Return
State0
            GoIfInOn    Switch,Set1
            Return
Set1
            On          Light
            SetMem      OnOffState,1
            Return
State1
            GoIfInOff   Switch,Set2
            Return
Set2
            SetMem      OnOffState,2
            Return
State2
            GoIfInOn    Switch,Set3
            Return
Set3
            Off         Light
            SetMem      OnOffState,3
            Return
State3
            GoIfInOff   Switch,Set0
            Return

As an exercise, copy this code and use it to replace the OnOffSwitch task in the previous complete program, then single step it in the simulator until you understand how it works.

Which is better, Branch/Target or Suspend/Resume?

There is no clear cut answer to that question. I think Suspend/Resume is a conceptually easier to understand and can save you some typing, but Branch/Target is perhaps a bit more versatile. If you are only a very occasional SPLat programmer, use Suspend/Resume but remember Branch/Target can be used for things other than multitasking. If you expect to be doing a lot of SPLat programming, try them both.

After a while you will start to see the pros and cons of each method. For instance, with Branch/Target a task can have multiple entry points for external events (i.e. methods), each with its own Branch/Target table. That means it lends itself to situations where external events are "pushed" into it. The Suspend/Resume method, on the other hand, is better suited to looking out for things to do (pulling).

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.


Multitasking: Advanced topics

This section contains some more advanced topics.

Special note: This tutorial and the multitasking mechanism it covers have largely been superseded by the newer MultiTrack mechanism. MultiTrack achieves everything and more, with considerably less programming effort. The only time you would use this older multitasking mechanism would be if the controller you are using is unable to handle as many tasks as you need using MultiTrack.

In This Section

Multitasking: Tasks and Processes

Multitasking: Tasks as Objects

Multitasking: Timer tricks

Multitasking: Programming the OBLCD


Multitasking: Tasks and Processes

In the program in the previous section each of the two subroutines is acting as a more or less autonomous program, taking care of some aspect of the overall functionality of the program. While the example may be trivial, the concept is a very powerful one.

From now on I am going to refer to them as "Tasks" or "Processes" or "Objects".

These ideas about tasks, processes and objects are simply a way of viewing things. There is nothing to say you must program SPLat this way. It just happens to be the way I have found works best for a wide range of complex control applications.

The idea behind the terms task and process are that the overall control function you need to implement can be broken down into identifiable entities, each taking care of some aspect of the overall control.

I like coffee. Real coffee. Serious coffee. My kind of coffee comes out of an espresso machine, thick and black, in small cups. My idea of heaven would be (well, include) one of those espresso machines that automatically grinds the coffee and makes a fresh cup of Good Brew at the press of a button.

If I could build such a machine (my mechanical skills are really not up to it!), it would need at least the following parts:

  • A water reservoir with level sensor and empty alarm
  • A water heating tank with temperature control
  • A pressure pump
  • A coffee grinder
  • A control panel with a few menu choices like "Strong", "Interesting" and "Raise the Dead" and signals to tell me to remove the next cup from the machine or fill up the water reservoir.

The controller for all this will be quite complex. One way to manage that complexity is "divide and conquer". For each functional element of the machine define a corresponding program element that takes care of it. This is what we call a task or a process. You can then arrange the tasks into a block diagram, like the one shown here.

Multitasking: Coffee machine block diagram

The block diagram shows the commands and/or data that must flow between blocks. I have planned a central coordinating task, called the main sequencer. Its job is to orchestrate the whole thing. (I don't intend to fully develop this example, it is far too complicated for that. But it will serve as a vehicle to introduce several interesting concepts).

The arrows between tasks in this diagram represents an exchange of data and/or commands. For example, the main sequencer can command the grinder to grind a batch of coffee, specifying the quantity. The grinder in turn reports back with a "Done" signal or a "Fault" signal. The mechanisms for these command and data exchanges will be developed later.

Take a look at the Pressure Pump box. I have planned for an "input" to it to activate the pump, but there's no feedback from it to say the pumping has worked, or is working, or has failed. Does that mean I've overlooked something? In all probability, yes it does. The top level block diagram is a tool you use to work out what main tasks your program will contain, and how they will interact. Thus you would draw a box, call it pressure pump and then ask yourself "What command inputs does this thing need? What answers must it provide? What can go wrong? How will it detect and report problems?". A far as possible, think about the pressure pump as an isolated subsystem and formulate the rules that your program will need to apply to correctly manage the pump.

Have you noticed that we have gradually moved from the idea of a simple subroutine, to one a couple of screens ago that has two entry points, one for initialization and one for normal updates? I am now sneaking in ones with entry points (inputs?) and outputs. We have gone beyond simple subroutines and started using the term "tasks" or "processes". I am now going to introduce a further development of this concept, and call them objects.


Multitasking: Tasks as Objects

I like to call the function boxes in my block diagrams objects. This is an echo of Object Oriented Programming (OOP) in Information Technology. The use of the term for SPLat programs may be a bit presumptuous (there is no suggestion that SPLat has such OOP capabilities as inheritance), but it lets me borrow a few other terms and concepts as well.

An object is a lump of code that takes care of a particular task or process. An object can have several entry points (i.e. subroutine addresses or named lines that you can do a GoSub to). These entry points provide an interface for the object's Properties and Methods.

Properties provide for the passing of variable data. For example, the Coffee Grinder object has a property called CG_Done. For another task/object to access the property CG_Done, it must call the subroutine with the name CG_Get_Done. CG_Get_Done returns the requested information in the X register as a True/False value.

Methods are a way of sending a command to an object to do something. For example, the coffee grinder has a method called CG_Grind. Calling CG_Grind will result in the grinder starting up. At the time of the call, the required amount of coffee (for "Strong", "Interesting" or "Raise the Dead") is provided CG_Grind as a number in the X register.

Another concept that is central to this is Data Hiding.

Data hiding means is that no object must be allowed to directly access memory (RAM) or other resources that are managed ("owned") by another object, but must instead use the object's Properties or Methods to access its data. Why is this important? Consider the Water Heater object. Its interface to the rest of the program is defined by 3 properties: WH_SetPoint, WH_Ready and WH_Fault. The block diagram implies that a thermistor will be used for temperature sensing, and indeed (let's pretend that) the first version of our coffee machine uses a thermistor. Each time the Water Heater object is asked to supply the WH_Ready property, it looks at the current temperature, and returns True if 95°C or more, otherwise False. But one day, 6 months and 5000 coffee machines later, some bright spark in Purchasing decides to switch over to a new water heater design that uses a simple bimetallic thermostat. We no longer have a temperature reading, just a simple on/off thermostat switch. Clearly, the Water Heater object will need to be re-written. But providing it still returns a valid True/False from its WH_Ready property, nothing else in the program need change. If we had originally allowed other parts of the program to access the temperature directly, or worse still take their own thermistor readings, then we might be in for long sessions of re-writing little bits all over our program.

There will be times when you want to bend the rules on data hiding. It can sometimes seem absurd to have to create a subroutine that does nothing but Recall a memory variable and return. I bend the rules sometimes. And almost every time I bend the rules I later get caught out with a program change that is made more complicated as a result.

The one area where I do bend the data hiding is in configuration menus. The menu system usually needs to poke and prod at variables all over the place, and to have to worry about using properties and methods while coding a complex menu is beyond the pale. So I mentally declare the menu system to be a privileged function and let it poke and prod as much as necessary. This is why the configuration memory is shown on the previous screen with arrows going everywhere.

In summary: An Object is a bunch of code dedicated to performing a certain task. It may have several subroutine entry points. Each entry point corresponds to a Method or a Property. An object may own certain resources like RAM locations or I/O points. No other part of the program is allowed to access those resources directly, except a possible configuration function.

As a matter of good style and readability, an object should have all its code in one contiguous block, delineated by a clearly visible border such as a line of asterisks. At the start of the code there should be a block of comments that define the name, function, methods and properties. Here is an example of what the header comments would look like for a Water Heater object

************ Water Heater object ***********************
;Short name    WH_
;Purpose: Control the water heater, providing PID control of
;         temperature.

;Methods:

;         WH_Init            initialize

;         WH_Update          Recalculate the PID control

;Properties:

;         WH_SET_SetPoint    Call with desired target temperature in W in 'C

;         WH_GET_Ready       Return True in X if the water is hot

;         WH_GET_Fault       Return True in X if there is a fault

;Resources:

;         Thermistor input

;         Heater output

;         Any RAM named with the WH_ prefix

;Note:    Faults are 'over-temp' and 'taking too long to heat up'. They are latching

;         and get reset by a call to WH_Init. The heater is forced off by a fault

These comments are a capsule specification of the object. The code for the object should follow immediately after the header comments.


Multitasking: Timer tricks

Most control programs use time in some way or other. One technique I use, in association with the multitasking methods I've described, is "global timer ticks".

With the "main loop calling subroutines" structure, each task gets "run" exactly once for each time around the main loop. The timer ticks method exploits this. What I do is to insert into the loop a new task that uses a timer to set a semaphore for exactly one "lap" of the loop each time interval. Here is a basic example, with the time code embedded in the main loop:

MainLoop:
          ClrS       Tick100       ;Clear the 100mS timer tick
          Test       7             ;Has another 100mS expired?
          GoIfT      NoTick100     ;G/ not yet
          SetS       Tick100       ;Yes. Set the tick
          SetTimer   7,10          ;Restart the timer
NoTick100:
          GoSub      Task_1
          GoSub      Task_2
          GoSub      Task_3
... etc ..
          GoSub      Task_n

          GoTo       MainLoop

Now, every 100mS the Tick100 semaphore will be set for exactly one call to each of the Task_ subroutines. Any one of them that is interested can use the semaphore for whatever purpose it wants to. By counting ticks it can generate longer intervals.

In the following complete program I've made only one task, for illustration. It flashes a light on for 200mS and off for 800mS.

;The first 8 bytes of RAM I elect to reserve for semaphores

Tick100   sEQU       0

;I start "ordinary" RAM at 19 (after PermStore area)

FL_SuspAddr mEQU     19             ;2 bytes for suspend address

FL_Count    mEQU     21             ;Tick counter for the flasher

;I/O definition(s)

FlshO     oEQU       15             ;An MMi99 front panel LED

**************************

;initialization

          GoSub      FL_Init

****************************

MainLoop:

          ClrS       Tick100       ;Clear the 100mS timer tick

          Test       7             ;Has another 100mS expired?

          GoIfT      NoTick100     ;G/ not yet (still timing)

          SetS       Tick100       ;Yes. Set the tick

          SetTimer   7,10          ;Restart the timer

NoTick100:

          GoSub      FL_Update

          GoTo       MainLoop

******** Flasher *************************

; Flasher task

; Short name: FL_

;Function: Flash output FlshO 200mS on, 800mS off

;Methods:

;         FL_Init

;         FL_Update

;Uses:    Tick100 timer tick

FL_Init

          SetMem      FL_Count,1        ;initialize the counter to a "safe" value

FL_Susp1

          Suspend     FL_SuspAddr

          DecMGoIfNZ  FL_Count,FL_Susp1 ;Count the tick, test if counted to 0

          On          FlshO

          SetMem      FL_Count,2        ;Set counter for 200mS

FL_Susp2

          Suspend     FL_SuspAddr

          DecMRetIfNZ FL_Count          ;Count the tick, test if counted to 0

          Off         FlshO

          SetMem      FL_Count,8        ;Set counter for 800mS

          GoTo        FL_Susp1

FL_UpDate

          RetIfSF     Tick100           ;Do nothing if it's not tick time

          Resume      FL_SuspAddr

There are a few little tricks in this code:

  1. The timer tick is checked at FL_Update, before the Resume. In effect that means the whole task (apart from checking the tick) will only run on each timer tick (every 100mS). This can save a fair bit of code, providing you have a task that only needs to ever run synchronously with a tick.
  2. I initialized the counter FL_Count to 1. RAM is automatically cleared to zero, so without this initialization, the light would initially not come on for 25.5s (think about that one, try it!)
  3. In the FL_Susp1 segment I used a DecMGoIfNZ. In the FL_Susp2 segment I used a different count and test instruction. Study and compare the two instructions, and work out why and how the second one works in this program
In many of my programs I generate several ticks at different rates, typically 10mS, 100mS, 1S, 10S and 1min. I then use the fastest one possible for each interval I need to generate. For example, for a 1 minute interval I will count off 60 1S ticks, giving an accuracy of about 1S. On the question of accuracy, this technique will produce an error related to the latency between a timer expiring and the program "coming around the loop" and testing it. A similar scheme based on GetTick10 and/or GetTick100 (introduced in dialect 12) will not have cumulative errors. The main loop with tick generation then becomes:

MainLoop:

          GetTick100		;non-zero if there are any "unused" ticks.

          StoreS     Tick100

          GoSub      FL_Update

          GoTo       MainLoop

Not only is this more compact, it will be more accurate.

If you are using a board with a dialect earlier than 12, you can't use semaphores. However, you can still use timer ticks by using a 1-byte flag rather than a 1-bit semaphore. You would also have to use Branch/Target rather than Suspend/Resume to code the flasher task.

Note: The SuperTimer mechanism introduced in dialect 16 makes the above concept largely obsolete. It is still "valid", but SuperTimers are much easier to use.


Multitasking: Programming the OBLCD

Note: If you are using a controller with dialect 16 or later you can ignore this section. Such controllers have a fully buffered LCD driver, which eliminates the timing concerns addressed here.

It is important in multitasking programs never to hold up the processor for any amount of time. That may be a consideration with the Onboard LCD (OBLCD). It takes a certain amount of time to send stuff out to the OBLCD. See OBLCD: Timing considerations for details.

In many programs a few milliseconds delay, while the LCD is being updated, won't matter. For example, if you are writing a menu system for a device that cannot be operating while it is also being configured, then there is no problem. If on the other hand you have a time-critical controller with constant LCD updates, then you must take care. An example of this would be counting pulses from an encoder and updating the display in real time.

There are two OBLCD instructions designed for this situation: OBLCD_GoIfBusy and OBLCD_GoIfIdle.

The following code shows these being used in a task that constantly updates the OBLCD with the value in RAM location Count for as long as semaphore ShowCount is true (assume some other code controls ShowCount).

DC_Update:
         Resume         DC_Address
DC_0:
         Suspend
         RetIfSF        ShowCount
DC_1:
         Suspend        DC_Address
         OBLCD_GoIfBusy DC_1          ;Wait for OBLCD to be free
         OBLCD_ SetCur  1,5           ;Position cursor
DC_2:
         Suspend        DC_Address
         OBLCD_GoIfBusy DC_2          ;Wait for OBLCD to be free
         OBLCD_UDecDispMFW Count      ;Display count

         GoTo           DC_0

Some notes on this code:

  1. There is no initialization entry point. I am relying on the fact that a non-initialized resume address defaults to falling through (in this case to DC_0).
  2. I used a RetIfSF rather that a GoIfSF at DC_0. By doing a Return the suspend address is unchanged, so next time around it will come back to DC_0 again.
  3. If I didn't use Suspend/Resume in this function, and it was called very frequently, it would be almost guaranteed to consume about 6mS every time. That could slow down a critical program considerably.
  4. It generally turns out to be the most economical of code if you place a Suspend at each line that is the target of a GoTo. That means that sometimes the Suspend will seemingly be executed at an inappropriate time, like the one at DC_0 on the very first call. Usually that really doesn't matter.


The SPLat Expansion Framework

Previous Topic

Next Topic

In This Chapter

Introduction

The SPLat expansion framework

SPx register model

SPx communications instructions

UV transfer instructions

SPiceX instructions

SPice configuration instruction

Expansion support in SPLat/PC V6


Introduction

This guide describes and covers the use and programming of the SPLat expansion system introduced with 2nd generation SPLat products in 2001, starting with the MMi99 product. In general this material is not product specific, i.e. it covers the general principles of the expansion system, but not much that is specific to individual products.

Target audience

This guide is aimed at SPLat users who are interested in understanding the general principles of the SPLat expansion framework, and in learning how to use it. The material is fairly technical in nature, so you need to understand bits and bytes, hexadecimal notation, command structures and register models. You also need to understand the concepts of registers within processors, preferably SPLat's internal registers, plus the general ideas behind command driven devices.

Don't be concerned if you find this material too difficult. It is extremely unlikely that you will need it at all. At most, if you are about to use, or contemplating a SPLat add-on, it may pay to familiarise yourself with the summary chart.


The SPLat expansion framework

The SPLat expansion framework sets out the ways and means of providing on-board as well as external expansion modules for SPLat controllers. It defines hardware arrangements, internal features of the SPLat processor and matching programming instructions. It can support a whole spectrum of peripherals ranging from very simple, low cost add-ons all the way up to very sophisticated peripherals with built in intelligence.

A significant feature of the SPLat expansion framework is that it provides a versatile, open-ended programming method. The result for you is that we can create a new expansion board and it will instantly be supported by all existing programming tools, including the existing SPLat/PC programming toolset. With a relatively little effort we can then give it its own tailored instructions by a simple extension of the SPLat/PC programming software.

In keeping with always delivering the most cost-effective possible control solutions, the SPLat expansion framework is designed to place the lightest possible cost burden on the base controller, and to provide a range of cost and performance choices. We don't call it a standard; it is more a range of ways in which expansion boards can be implemented. There is no standard for board shapes and sizes, there is no standard power supply system and even the expansion connector can differ from board to board. The framework does however define a consistent programming model and interface, which lets us to you with sophisticated programming support even on custom peripherals.

This chapter describes the hardware basics of the SPLat expansion framework and introduces the several different schemes that comprise the framework. These range from the very simple "Touch of SPice" boards up to an open-ended system of addressable peripherals called SPx.

Previous Topic

Next Topic

In This Chapter

Expansion connector

Summary of expansion schemes

Touch of SPice

Tiny Serial Peripheral (TSP) interface

Xwire

SPiceX

XPice

External SPx peripherals

Onboard SPx peripherals

Virtual SPx peripherals

UV SPice

Power supplies


Expansion connector

Most standard SPLat boards designed from 2001 on (starting with the MMi99) will have two expansion connectors. These connectors will support two different sets of expansion connections:

SPice connector

The processor chips we use in SPLat have a lot of I/O pins, typically 40-50. Very many designs do not use up all these pins. By bringing unused pins out to the SPice connector, we make them available for expansion purposes, where they are used with what we term SPice boards.

What kind of pins are otherwise unused, and how many, will vary from one SPLat board to another. That is why there may be considerable difference between the SPice expansion capabilities of various SPLat boards, and why a SPice expansion board will generally only be suitable for hosting by one given SPLat controller board.

SPx connector

The SPx connector carries what is known as the SPI bus, plus some additional pins, and provides support for several diverse expansion schemes.

The SPI bus is an industry standard 3-wire high speed Serial Peripheral Interface. The processors we use in SPLat include built in SPI functionality.

SPI, in its native form, provides a rudimentary mechanism for shifting data in and out of a processor one byte at a time. On its own, SPI can provide basic I/O expansion with simple shift register chips. We use basic SPI in what we call SPiceX mode, where it allows modest expansion at very low cost. For the more advanced capabilities we use SPI plus a proprietary protocol, and call it SPx. At an intermediate level we use SPI, plus a strobe line, in XPice mode.

Virtually every future standard SPLat board will have the SPx connector.


Summary of expansion schemes

The following table summarizes the attributes of the different SPLat expansion schemes. Please note that this is a guide only, not hard and fast rules.

 | Xwire; (2007) | Touch of SPice |  | SPiceX | XPice | SPx Peripherals | Onboard SPx, UV-SPice, Virtual SPx
 |  | TSP Discontinued | Simple SPice
Suitable for | Networking multiple controllers and dedicated peripheral boards | Smart I/O and communication functions | A few digital or analog I/Os | Bulk I/O or simple peripherals | Bulk, dumb digital I/O expansion | Intelligent peripherals | High-level onboard functionality
Based on | Half duplex serial data. Master/slave system | Spare processor I/O pins brought out to a connector. |  | Raw industry standard SPI bus |  | SPI interface, SPx programming | SPx programming model but without SPx hardware
Number and type of I/Os | Practically unlimited | Limited by board size. Can be complex | Depends entirely on the controller board | 8 to 100+, digital |  | Open-ended (none for Virtual SPx)
Programming | Simple configuration, then simply exchanges data automatically in background | Special TSP communication instructions | Appear as simple I/O points. Same as basic SPLat programming | Simple instruction to block transfer 1-20 bytes | Automatic configuration. Appears like normal onboard I/O | Register/command based programming model. The peripheral can provide high level functionality but usually requires a sophisticated programmer.
Number of possible add-ons | Depends on cabling length | 1 | 1 | Usually only 1 | Several, depending on the details |  | Determined by custom board specification
Mechanical | Separate boards, simple wiring. | Plugs onto a standard board. Can also be designed to work on the end of an extension cable |  |  | On an extension cable. |  | Integral part of SPLat board
Power supply | Usually requires power supply to its own specs | Usually from host SPLat board |  |  | Usually requires power supply to its own specs

Notes:

  1. TSP and Xwire are implemented only on selected standard and custom controllers. Please refer to individual product documentation.


Touch of SPice

Touch of SPice is the name given to the conceptually simplest type of expansion board. These are also referred to simply as "SPice" boards.

A SPice board is designed to take advantage of the SPice pins on the expansion connector, i.e. those spare processor I/O pins that we have brought out to the expansion connector. Therefore SPice boards will be specific to a given SPLat controller. For example a SPice board designed for the MMi99 will not necessarily work with other controller boards (though we have made the SPice connector on the SL99 identical).

The huge advantage of SPice boards is that they are relatively simple to design and make, and simple to program. That means we can economically engineer even a small batch of SPice boards for your needs.

Some SPice pins on some SPLat boards are capable of taking on different roles, for example digital output or analog input. This is the case with the MMi99 (in general we always try and bring out pins with the most functional flexibility). Such pins are configurable via the same programming model as the more sophisticated SPx peripherals.

SPice boards can be made either to plug "piggy back" directly onto the host SPLat board, or to connect via an extension cable. In general you can only use one SPice board at a time.


Tiny Serial Peripheral (TSP) interface

The Tiny Serial Peripheral interface was introduced in May/June 2004.

TSP is based on having a small add-on board, like a simple SPice board, with its own processor. The TSP board communicates via a low speed serial data link to the main SPLat processor. The data exchanges consist of variables in bit, byte, word (16-bit) or Long (32-bit) formats. There is also provision for strings up to 16 bytes in length. Data is exchanged via the UV registers.

The TSP peripheral will contain a given number of variables, held in its memory. The SPLat processor can poke data into those variables and peek at (read) the data in them. What the data is, does or means depends on the individual TSP peripheral board.

We classify TSP as a type of SPice board simply because it uses the same SPice connector. The functionality is actually considerably more sophisticated. Initial TSP products will include an embedded web server, a multiple high speed quadrature encoder interface with comparison registers and a 2-channel 12-bit thermocouple interface.

The TSP is programmed using a special group of instructions that can exchange the various data types.


Xwire

Xwire was introduced August 2007. It allows several controllers and/or dedicated function peripherals to be connected together over a simple 2-wire bus. Xwire documentation.


SPiceX

Every expansion connector (on standard boards) will expose the SPI bus. The simplest way in which the SPI bus can be used is with a very simple I/O expansion system based on shift registers.

The SPI bus on the expansion connector can be used for shift register I/O expansion. This mode is referred to as SPiceX, because it is a simple scheme with limited capabilities, but very simple to implement.

Programming of SPiceX is achieved by a simple adaption of the SPx programming model described later. In its unadorned form it will require a fairly sophisticated programmer to handle it. Tutorial

SPiceX is best suited to providing fairly large amounts of digital I/O. It is well suited to solutions based around available SPI-compatible chips, for example multi-segment LED drivers. It has the advantage of very simple hardware, but on the downside it will not provide on-screen simulation of those I/Os, and programming will be less convenient than the basic built in I/O.

There is no reason why we can't make expansion boards that combine SPice and SPiceX. Usually there can only be one SPice/SPiceX board connected to a SPLat.


XPice

XPice permits plain vanilla digital I/O expansion that is both economical and blends virtually seamlessly into the programming.

XPice is a kind of super SPiceX. With XPice we combine the hardware simplicity of SPiceX with the simple programming of I/O points that are part of the main controller board.

A XPice board is a simple shift-register based bulk I/O expansion board. Most XPice boards are capable of being cascaded (daisy-chained), so you could have more than one hung off a SPLat controller. The XBIO16, HIO16 and XIRO16 expansion boards are XPice.

XPice enabled controllers will automatically recognise XPice expansion boards when they are connected. They will also report themselves to SPLat/PC with the expansion included.

The controller will appear on the SPLat/PC screen with the additional I/O showing in the I/O window, and the additional I/O can be programmed with regular instructions like GoIfInOn, INPUT, ON, OFF etc.


External SPx peripherals

The SPx peripheral concept is the "highest level" of the SPLat expansion framework. SPx is a proprietary adaption of the industry SPI bus that allows for many addressable peripherals. It is our intention to eventually publish the full SPx specification to permit 3rd-party peripherals.

With SPx the external peripheral (add-on) must have its own processor, simply because the addressing and communications scheme requires intelligence. SPx peripherals can therefore provide high levels of functionality, such as LCD operator consoles, multi-axis motion controllers etc.

SPx peripherals can be visualised (modeled) as containing a number of registers which can be sent commands to execute and data to act upon, and other registers that can be polled (interrogated) for their data. Once sent a command and perhaps some data to work with, the peripheral can go off and "do its thing" without intervention from the SPLat board. This opens up an almost unlimited scope for intelligent peripherals that can perform complex functions with the SPLat only having to provide high level control.

There are over 32,000 possible SPx addresses. Addresses are pre-allocated to specific peripherals. If a setup is likely to need more than one of a particular peripheral, a block of say 16 addresses will be assigned to that peripheral type. The addressing scheme is set out elsewhere, and contains a number of special features. Some addresses are reserved for 3rd party peripherals. Of these, some are "free for all" and others must be registered with us to avoid conflicts.

One SPLat board can control several SPx boards.


Onboard SPx peripherals

The SPLat expansion framework is designed to handle not only off-board peripherals such as Touch of SPice boards and SPx peripherals, but also onboard peripherals. An onboard peripheral is a peripheral function that is part of the main SPLat board, but programmed as if it were an SPx board. This gives a neat path for advancing from of-the-shelf solutions to full custom designs.

Take the case of a hypothetical external add-on board, a Time of Day Clock (TDC), that uses the SPx bus. This device has a number of registers, and actions commands that are sent to it via the SPx bus. It also has a particular address.

Suppose now that a SPLat customer needs that TDC functionality in a custom controller. He has developed his product using a standard SPLat board, including the standard TDC. What we will do is to build the TDC circuitry onto his custom board, without its own processor, and let the main SPLat processor look after it. For us it is a minimal amount of work to "port" the driver Firmware from a peripheral processor to the main SPLat processor, while for the customer his existing SPLatWare investment is protected and he sees a significant cost saving. The onboard implementation of an SPx peripheral will look exactly the same to the SPLatWare programmer, with the exception of changing from an offboard to an onboard address.

A special case of an onboard SPx peripheral is the LCD port that will be a standard feature of many future standard SPLat board, starting with the MMi99. This port supports an industry standard LCD character display, with size ranges from 2 lines by 16 characters up to 4x20. Although physically located on the board, and having no processor of its own, it uses the same programming as if it were located on a separate board with its own processor.


Virtual SPx peripherals

By extending the concept of the onboard SPx peripheral one step further, we get the idea of a Virtual SPx Peripheral. This is an add-on function in the SPLat that exists purely within the Firmware and on-chip resources of the processor chip. What we can in effect do is create a program that performs some specialised function, locate it in the same chip as the SPLat processor, and connect between it and the SPLat function using the SPx programming framework.

The benefit of this compared to simply adding to the SPLat instruction set lies in the fact that is allows modular extensions to the basic SPLat capability. In effect it is a model that allows us to add custom instructions (i.e. processing capability) to the processor without cluttering up the base instruction set. In the modern idiom it is an object oriented approach to expandability.

The OBCA function is a virtual SPx peripheral. A more elaborate example would be a (hypothetical) onboard data logging function. The virtual data logger would use part of the chip's Flash memory as a logging memory, and would have its own command set for accessing that memory. Implementing this within the SPx framework means that if you need more logging memory than exists on the chip, we could make you a custom board with a HUGE memory, and your existing SPLatWare investment would be protected.


UV SPice

The UV SPice model is a hybrid of the Touch of SPice plug-on hardware and onboard SPx peripheral. It allows a plug-in board to be designed that doesn't have a processor but is programmed using the UV model.

This would be used where we need fairly simple hardware but complex Firmware and a sophisticated programming interface. It would typically be used in a custom SPLat where an optional add-on is required, because it needs a device-specific driver in the controller's firmware.

It may not always be possible with such a board to provide an "Are you there" response, because the SPLat processor may have no way of knowing if the add-on board is present.


Power supplies

The expansion connector on a SPLat board carries two power supplies: The raw power input provided to the board and the board's internally generated 5V supply. On some boards we may make any other voltages available.

The 5V supply on a board is designed mainly to supply the needs of the board itself, be it 50mA or 250mA. There will usually be only a small amount of spare capacity. The raw power can vary from board to board, on some boards 10-24VDC, on others 9VAC, 18VAC or 24VAC.

This leads to restrictions on the power supplied to an expansion board.

SPice and SPiceX boards, especially ones that plug piggy back directly onto the host SPLat board, must by definition make do with whatever power they get from the host. This could place constraints on allowable supply voltage supplied to the SPLat board when used with a given SPice board.

SPx boards will more often than not require a power supply that meets the SPx board's specification.

This all means that when deciding on a power supply you must consider both the needs of the SPLat board and of the expansion board(s). Please consult the product documentation or ask us.


SPx register model

The SPx register model holds the key to programming SPx peripherals. It is consistently used across external SPx Peripherals, Onboard SPx Peripherals, UV SPice and Virtual SPx Peripherals. It is also used for SPiceX add-ons, but in a slightly different way (See SPiceX communications instruction). The register model is illustrated in the accompanying diagram.

SPx register and instruction model

The key element in the SPx register model is two registers called U and V. U is a 20-byte register that is used to pass data between the SPLat processor and the peripheral. V is a 1-byte register that is used mainly as a byte counter (indicating how many bytes are to be moved). We frequently refer to the pair as "the UV registers" or simply "UV".

Several important concepts underpin the UV-based SPx model:

  1. All traffic between the traditional SPLat registers (far left on the diagram) and the peripherals is funneled through UV.
  2. There are two distinct groups of SPLat instructions associated with UV. One for transferring data between the SPLat registers and UV (UV transfer instructions), and one for communicating between UV and the peripherals (SPx communications instructions). The communications instructions result in messages being sent to the peripheral and resulting responses from the peripheral.
  3. There is a fixed number of instructions in each group. They do not vary in number, form or content. The ultimate outcomes of those instructions depends entirely on the peripheral. Hence, instruction "Foo" addressed to peripheral A means something quite different from instruction "Foo" addressed to peripheral B.
  4. Traffic (messages) from UV out to a peripheral contain data and implied commands that will trigger actions in the peripheral. The SPLat processor does not know what those actions are. However, for a given outgoing message type, every peripheral must respond with a return message of an exactly prescribed form. That means that when the SPLat sends out a message, it knows in advance exactly what the format of the response will be, exactly how much data it will contain and exactly what to do with the data. And what it does is always to place it in UV in a prescribed manner.


SPx communications instructions

This chapter details the generic SPx communications instructions. These are the basic instructions that control command and data communications between the UV register pair inside the SPLat processor and SPx peripherals.

These are generic instructions. Most peripherals will eventually get their own specific instructions as well. Those instructions will be synthesised by SPLat/PC using the generic SPx communications instructions in this chapter, the generic UV transfer instructions and regular SPLat instructions.

The SPx communications instructions all combine an implied command to a peripheral with a set amount of data. There are 5 instructions for outgoing commands and data and 4 to interrogate (poll) the peripheral for incoming data. Each instruction deals with a different amount of data. One of the 9 instructions supports 128 command codes, while the rest support 16 codes. A small number are reserved for fixed system related use.

When the SPLat processor executes an SPx communications instruction, it composes and sends a message to the peripheral. The message will contain the peripheral address (which is an instruction argument), a command code (which is also an instruction argument) and some data. The data comes from UV as prescribed by the instruction itself. Once the processor has sent the message, it waits for a response from the peripheral. The addressed peripheral will always respond, even if the message is not one it supports (in the event of a program bug). The normal response will always contain a prescribed amount of data, which the processor will save in UV. There is also a mechanism for error handling, i.e. an abnormal response or no response.

There are instructions for transferring 0, 1, 2 or 4 bytes of data, plus one in either direction for variable length data blocks (1 to 20 bytes).

The following sections describe the communications instructions. The instruction is summarized in the section heading. The following nomenclature is used:

Mnemonic cc,aaaa [In] or [Out]

Mnemonic is the key word recognised by the SPLat development environment (SPLat/PC or SPLat/IDE).

cc is the command code. This can be a number between 0 and 127 in the instruction that has 0 bytes of data and between 0 and 15 in the remaining instructions. Each command code cc results in a different response in the peripheral.

aaaa is the 16-bit peripheral address. This selects a particular peripheral as the target for the instruction. Addresses are allocated by us and will normally be locked into the Firmware driving the peripheral.

[In] or [Out] denotes which direction the resulting data will flow. [Out] means data is transferred from UV to the peripheral. [In] means it is transferred from the peripheral to UV.

U(n) denotes byte n of U, counting from 0, i.e. the 20 bytes of the U register are designated U(0) through U(19).

Previous Topic

Next Topic

In This Chapter

SPxCmd0 cc,aaaa [Out]

SPxCmd1 cc,aaaa [Out]

SPxCmd2 cc,aaaa [Out]

SPxCmd4 cc,aaaa [Out]

SPxChOut cc,aaaa [Out]

SPxPoll1 cc,aaaa [In]

SPxPoll2 cc,aaaa [In]

SPxPoll4 cc,aaaa [In]

SPxChIn cc,aaaa [In]

Error handling in SPx communications instructions


Error handling in SPx communications instructions

There are two possible error conditions resulting from an SPx communications instruction: Missing SPx peripheral and invalid command sent to a SPx peripheral. Both are fatal errors.

If the addressed SPx peripheral is not present there will no response to messages. The SPLat processor will make 10 tries on a message before giving up. It will then execute a run time error restart, recording error type (Missing SPx peripheral) and program address.

There is one exception, which is the "Are You There" message (SPxCmd0 with cc=127). If this fails to get a response after 10 tries, the SPLat processor will set U(0) to False and then continue. This provides a mechanism for your program to determine the presence of possibly optional SPx peripherals.

If the addressed SPx peripheral is present but is sent a command that is not valid for it, or which it cannot currently execute (the peripheral might be busy or in the wrong state) it will send back a NACK (Negative ACKnowledge). This will be a result of a SPLatWare programming error. The SPLat processor will make 10 tries on a message before giving up. It will then execute a run time error restart, recording the error type (Invalid command to SPx peripheral) and program address.


UV transfer instructions

This chapter details the generic UV transfer instructions. These are the basic instructions that control transfers between the main part of the SPLat processor (X, the floating point registers and RAM) and the UV register pair.

These are generic instructions. Most peripherals will eventually get their own specific instructions as well. Those instructions will be synthesised by SPLat/PC using the generic UV transfer instructions in this chapter, the generic SPx communication instructions and regular SPLat instructions.

There are 17 or so UV transfer instructions. They provide a comprehensive repertoire of ways of moving data between SPLat's main registers and UV, in both directions.

The following sections describe the transfer instructions. The instruction is summarized in the section heading. The heading shows the mnemonic following by placeholders for the required arguments. The arguments are detailed in the body text.

Previous Topic

Next Topic

In This Chapter

ClrU

PushU n

PopU n

PushV

PopV

WtoU n

UtoW n

UtoQ n

QtoU n

SetV i

SetU n,i

StringToUV n,"String"

MemToUV m,n

UVToMem n,mm

GoIfUT n,m,line

GoIfUF n,m,line


SPiceX instructions

Previous Topic

Next Topic

In This Chapter

SPxMode m

SPxTxfrU p


SPice configuration instruction

There is one special SPLat instruction associated with Touch of SPice boards.

    SPiceConfigU

This is used where the SPice pins are configurable as to their functions. It uses the contents of U to configure the SPice pins. The number in U(0) configures the first pin, the number in U(1) configures the second pin etc. The values are:

0 = No change. The pins configuration remains unaltered

1 = Digital Output

2 = Digital Input

3 = Analog Input

4 = Pulse Width Modulated (PWM) Output

5 = Digital Bidirectional

6 = Counting Input

Other numbers could also apply. This mechanism is entirely a function of the individual SPLat board, so you need to refer to the documentation for the board you are using. The definition of pin numbers is also product dependent (but will not necessarily be the same as I/O numbers).


Expansion support in SPLat/PC V6

SPLat/PC V6.9 has been introduced to provide support for the SPLat expansion schemes. The following features are provided:

  • A new window to represent the UV register pair.
  • Translation of UV transfer instructions and SPx communications instructions
  • Simulation of UV transfer instructions.

It does not simulate SPx communications instructions.

Previous Topic

Next Topic

In This Chapter

UV simulation window in SPLat/PC 6.9

Onboard LCD programming

MMi99 SPice support in SPLat/PC 6.9


UV simulation window in SPLat/PC 6.9

UV simulation window gif

The UV simulation window is illustrated. The small pane in the top lefthand corner represents V. The large pane represents U. U is also shown at the bottom in text format (for those bytes that are readable ASCII codes.). The small number to the left of the large U window is the U index.

The main U and V windows can be edited. All editing is in hexadecimal. There is no option for other formats.


Onboard LCD programming

SPLat/PC V6.9 contains programming support for the onboard LCD port. This is documented elsewhere.


MMi99 SPice support in SPLat/PC 6.9

SPLat/PC 6.9 supports the MMi99 by representing the SPice pins in the I/O and Analog windows. Because the SPice pins are all multi-function configurable pins, they all appear as digital inputs, digital outputs and analog pins. SPLat/PC does not know about pin configurability, so you must not rely on it to prevent you from making mistakes.


Xwire

Xwire was introduced August 2007. It allows several controllers and/or dedicated function peripherals to be connected together over a simple 2-wire bus. Xwire documentation.

Previous Topic

Next Topic

In This Section

Xwire: Introduction

Xwire: Hardware requirements & wiring

Xwire: Programming

Xwire: Error handling

Xwire: Speed, range and capacities

Xwire: SPLat/PC Xwire window


Xwire: Introduction

Xwire is an easy to use, low cost serial data bus scheme for connecting together several compliant SPLat controllers and/or peripheral boards and exchanging data between them. Xwire was released in August 2007.

The above diagram shows the logic of an Xwire one-on-one master/slave connection. The basic idea is simply that a small block of RAM in the master gets replicated in RAM in the slave, and visa versa. If the master program writes some data to its "Tx RAM", the same data will appear some time later in the slave's "Rx RAM". If the slave writes some data to its "Tx RAM", it will appear in the master's "Rx RAM" some time later. The mechanics of transferring data between boards is taken care of automatically by the Xwire system.

What the data means is entirely up to the two programs.

The diagram shows one master and one slave. A system may contain several slaves, though only one master. The slaves can be other controllers or special purpose Xwire peripheral boards. The Master would generally contain a separate Tx RAM block per slave, and must contain a separate Rx RAM block per slave.

Xwire has two aspects to it: The Xwire electrical interface and the Xwire data protocol. The protocol is the "language" that is used to exchange data between boards. You don't have to know either of these. What you may want to know about is that the Xwire protocol can optionally be used with the industry standard RS485 electrical interface. RS485 does not have a protocol but can be used with any one of a large number of protocols such as Xwire or ModBus. To use "Xwire over RS485" you will usually need additional converter hardware, but it will allow you to go much further between boards.


Xwire: Hardware requirements & wiring

Xwire port

In its basic form Xwire is intended for use with a simple twisted pair connection to the Xwire port provided on certain SPLat products. The Xwire port can appear in different forms:

  • On the CC18 and HD8 controllers it comprises two 2-pin connectors labelled X1 and X2. The two are connected in parallel on the board. The duplication is simply to simplify wiring up several CC18s.
  • On the MS120 controller there is a single 2-pin connector labelled X1.
  • On Xwire peripheral boards one or two 2-pin connector(s) labelled X1 and X2. These generally also have an onboard RS485 interface that can be jumper selected.
  • The SL100 was designed before we thought up Xwire. However, the signals can be brought out of the SPice connector using a special wiring arrangement.
  • Early MMi202 had Xwire on the SPice connector, like the SL100.
  • The MMi202A has dedicated Xwire connectors.

The Xwire port has 2 pins. One is ground. The other is a bidirectional data line and can be in transmit mode or receive mode.

Xwire wiring

All Xwire ports in a system are simply wired in parallel, like pin to like pin. On boards with two connectors (say CC18), connectors X1 and X2 are interchangeable and can be used for daisychaining. The interconnecting cables can be made with the two wires lightly twisted together, say one twist every 25mm. You can also use screened single core cable, which is better for noise immunity but takes more effort to make.

The above diagram shows the suggested wiring arrangement for 2 or more controllers connected by Xwire. The Xwire connectors are interconnected with twisted pair cables. For the boards illustrated there are X1 and X2 connectors. These are interchangeable and connected together on the boards.. In effect the 3 boards have their Xwire ports wired in parallel. Notice that we have shown the power supplies for the boards wired individually back to the power supply. This is called a star arrangement and is less likely to have noise problems that a bussed (parallel) power distribution scheme. What you absolutely must not do is get lazy and let the Xwire ground circuit carry power supply return (ground) currents. Keep the power supply ground wires as heavy and as short as you can.

If you are wiring together several boards that have only one Xwire connector, say MS120s, you will have to make Y cables (2 wires in each pin).

In the diagram above we have shown how you would go about using Xwire in a vehicle. This would typically be one SPLat in the cabin (the lefthand one) and one "down the back of the truck". Only the latter is switching heavy loads. The SPLat in the cabin would typically be an MS120 used as an operator interface. It would be the master controller and not switch any heavy loads.

There are two other important aspects of this scheme:

  1. There is only one ground connection.
  2. The Xwire signal is run through shielded cable (and definitely not wrapped around the ignition coil or alternator!)

This is not a recommendation to use standard SPLat products in an automotive environment. You should consider it a technique to use for Proof of Concept work. We do make custom SPLat controllers for automotive use, and they have a significant amount of additional built in protective circuitry.

Wire dress

The Xwire wiring should be kept well away from any wires or objects that can generate electrical noise or "spikes". This includes such things as

  • Relays and contactors (anything that makes sparks, inside or outside)
  • Electric motors
  • Variable Frequency Drives (VFD, VSD)
  • Radio transmitters
  • Triac and SCR circuits.
  • Power wiring to/from any of the above.

In general it is a good idea to keep the Xwire wiring close to the DC power supply wiring, so large loops are not formed that can pick up noisy magnetic fields.

When testing wiring arrangement you can use the XwireGetErrCount instruction to good effect. If your controller has a display, put the count on the screen. Otherwise store it in RAM and use SPLatLink (the SL button on the SPLat/PC module window) to read it out.


Xwire: Programming

Case 1: Interconnecting 2 or more controllers

Programming several SPLat controllers to exchange data via Xwire is extremely simple. It consists of the following steps:

  1. Decide which is to be the master. There can only be one master. The master can talk to all slaves but slaves cannot talk directly to each other.
  2. Arrange for the data you want to exchange to be in contiguous Tx and Rx blocks in RAM. Use mEQU directives to define absolute RAM addresses. You can also use use defXXX (defBYTE, defFLOAT) etc. providing you also use defBLOCK to ensure you get contiguous areas of memory.
  3. Set Xwire addressing in your slave board(s). Your SPLatWare program must usually do this (though hardware jumpers may also be involved). In some cases the Firmware loaded into the board may also come into the equation. Refer to the Xwire documentation for the board you are using.
  4. Create Xwire configuration tables in NVEM0. These tell each slave board where its Tx and Rx RAM blocks are located. In the master they also tell it which slaves (addresses) to expect.
  5. In the initialisation code of your programs, invoke the Xwire configuration table.

Case 2: Xwire peripheral board(s) connected to a master controller

We make a number of boards that we call Xwire peripherals. Some have quite sophisticated functionality in their own right, and work as semi-autonomous subsystems. An Xwire peripheral is always a slave, but we have pre-programmed it to do its job.

  1. Study the Xwire peripheral's product documentation to learn how its Xwire address is set and how its Tx and Rx data is structured.
  2. Arrange for the data you want to exchange to be in contiguous Tx and Rx blocks in RAM in the master controller (all that is taken care of automatically for the Xwire peripheral). Use mEQU directives to define absolute RAM addresses. You can also use use defXXX (defBYTE, defFLOAT) etc. providing you also use defBLOCK to ensure you get contiguous areas of memory.
  3. Set Xwire addressing in your peripheral board(s). Refer to the Xwire documentation for the boards you are using.
  4. Create Xwire configuration tables in NVEM0. These tell each slave board where its Tx and Rx RAM blocks are located. In the master they also tell it which slaves (addresses) to expect.
  5. In the initialisation code of your master program, invoke the Xwire configuration table.

Hint: It is always a good idea with an Xwire hookup to start with a very basic connection, with the minimum amount of programming, to show it is working. For example, with a thermocouple Xwire board SX10500, make a very basic program that just displays the temperature on the LCD, if you have one, or stores in RAM for readout via SPLatLink (The SL button in SPLat/PC's Module window) if you don't have an LCD. Do this as a small test program, with no other functionality. If you are using multiple Xwire slaves, do it separately for each one, and only when you have them all tamed start combining them. One of the biggest sources of problems is when people try to get too may things working all at the same time. Do it one thing at a time. Divide and conquer.

Previous Topic

Next Topic

In This Section

Xwire: Instruction summary

Xwire: Master configuration

Xwire: Slave configuration

Example: Simple I/O expansion using Xwire

Xwire: Addressing

Xwire: Some hints and ideas


Xwire: Instruction summary

Xwire is controlled by the following instructions, available in dialect 20 and later in boards that support them:

XwireMaster pppp

Make this controller the Xwire master and use the configuration data table stored at address pppp in NVEM page 0.

XwireSlave pppp

Make this controller an Xwire slave and use the configuration data stored at address pppp in NVEM page 0.

XwireSetAddr

Use the value in X as the Xwire address of this controller. Pops X. Meaningless if this is not an XWire slave. The legal address range is 0 to 253. The master does not have an address.

XwireGetJumpers

Some boards (notably CC18) contain jumpers intended for setting the Xwire address. This instruction reads the jumpers into X.

Note 1: The connection between the jumpers and the address is purely via the XwireSetAddr instruction. It is not automatic.

Note 2: If the jumpers are not used for setting the Xwire address they can be used for anything else. Your simply need to use XwireGetJumpers to read them as they do not appear as regular inputs.

XwireGetErrCount

In the master, push the error count into X and reset the count. This will give a count of how many transmission errors have been detected since the previous time this instruction was executed.

Boards with dialect 23 or later support the following additional instructions:

XwireStop

In an Xwire master executing this instruction will turn off Xwire activity at the completion of the current Xwire transaction.

XwireIdlePoll

Test if Xwire polling has ceased following an XwireStop instruction

XwireGetPollCnt

Determine the level of Xwire activity. A slave can check if it is being polled. A master can find out how frequently it is polling.


Xwire: Master configuration

In the Xwire master you need a table in NVEM0 that defines all the data connections that are to take place. The tables in master and slave(s) must be consistent with each other, or it won't work.

In the master the table contains one 5-byte entry per slave. The entry defines the slave address, and the address and block lengths within the Master of the Tx and Rx RAM blocks for the slave. The general format is as follows:

        NVEM0

XWireTab:

        NV0Byte         SlaveAddr0,TxRAMStart0,TxRAMLen0,RxRAMStart0,RxRAMLen0

        NV0Byte         SlaveAddr1,TxRAMStart1,TxRAMLen1,RxRAMStart1,RxRAMLen1

        NV0Byte         SlaveAddr2,TxRAMStart2,TxRAMLen2,RxRAMStart2,RxRAMLen2

... etc

        NV0Byte         255    ;End of table sentinel.

SlaveAddr0, SlaveAddr1 etc are the Xwire addresses of the slave 0, slave 1 etc. Legal values are 0 to 253.

TxRAMStart0, TxRAMStart1 etc are the addresses in Master RAM where data is to be taken from and sent to the slaves.

TxRAMLen0, TxRAMLen1 etc are the number of bytes that are to be sent to the respective slaves.

RxRAMStart0, RxRAMStart1 etc are the addresses in master RAM where data received from the respective slaves is to be stored.

RxRAMLen0, RxRAMLen1 etc are the number of bytes to expect (and store in RAM) from the respective slaves.

The final 255 is vital. It signals the end of the table.

Notice that the master does not know where in the slave the data is located.

Example:

Suppose we have two slaves. The first slave is to receive 2 bytes of data from the master and send back 3 bytes of data. The slaves are set to Xwire bus addresses 8 and 76. We decide that data to the first slave will be taken from local (master) RAM address 19 and 20, and data from the first slave will be stored in RAM at 21 through 23. Our table might look like this:

        NVEM0

XWireTab:

        NV0Byte         8,19,2,21,3    ;First slave

        NV0Byte         76,57,5,85,2   ;Second slave

        NV0Byte         255            ;End of table

Can you work out where the data to and from the second slave are located? How many bytes are transferred in each direction? (Answer at the bottom of the page).

Normally you should use symbolic names, so it might look more like this:

S0TxRAM:        mEQU    19,2    ;Define Xwire Tx RAM for slave 0

S0RxRAM:        mEQU    21,3    ;Define Xwire Rx RAM for slave 0

S0Address:      EQU     8       ;Xwire address of slave 0

S1TxRAM:        mEQU    57,5    ;Define Xwire Tx RAM for slave 1

S1RxRAM:        mEQU    85,2    ;Define Xwire Rx RAM for slave 1

S1Address:      EQU     76      ;Xwire address of slave 1

        NVEM0

XWireTab:

        NV0Byte         S0Address,S0TxRAM,2,S0RxRAM,3

        NV0Byte         S1Address,S1TxRAM,5,S1RxRAM,2

        NV0Byte         255

The configuration will be invoked by the following instruction placed in the early initialization code for the master

        XwireMaster    XWireTab

-----------------------------------------------

Answer to question: We send the second slave 5 bytes starting at 57. We expect back 2 bytes which will be stored at 85 and 86.


Xwire: Slave configuration

Configuration of slave controllers

A SPLat controller that is to be used as an Xwire slave needs two things to get communications going:

  1. An Xwire address, so the master knows how to find it
  2. A configuration table in NVEM page 0 that tells it where in RAM the exchanged data is to be located.

Setting slave address

The slave's address is set by executing an XwireSetAddr instruction with X set to the required address. This would normally happen during your program's initialisation. This must match the SlaveAddr used in the master configuration table. This could be "hardwired" into your program or perhaps derived from some inputs or jumpers. For example, the CC18 and HD8 have two jumpers, A0 and A1, which can be read into X with the XwireGetJumpers instruction.

Slave configuration table

Each SPLat controller that is a slave in Xwire must also have a configuration table in NVEM page 0. For a slave it is a single 4-byte entry of the form

        NVEM0
XWireTab:
        NV0Byte         TxRAMStart,TxRAMLen,RxRAMStart,RxRAMLen

The variables mean the same as before.

The RAM addresses in the slave need not match those in the master. However the slave TxRAMLen must match the master RxRAMLen, and visa versa. The master and the slave will both ignore any data transfers that contain the wrong number of bytes.

The configuration will be invoked by the following instruction placed in the early initialization code for the master

        XwireSlave     XWireTab

Dedicated slave boards

An Xwire slave needn't be a SPLat controller. It can simply be a dedicated peripheral device that interfaces to Xwire. In that case you won't need to do anything to configure it, except set some jumpers for its address. The board's documentation will tell you how many bytes of data to send it, how many to expect back, and what those bytes mean.


Example: Simple I/O expansion using Xwire

Let's consider a simple example of of just using a CC18 as an I/O expander for an MS120 (or for another CC18 for that matter). We will make it so all 16 I/O points are available both as inputs and as outputs. That means we have to send 2 bytes to the CC18 and receive 2 bytes back. Let's give the slave an address that is simply set by the Xwire jumpers. For demonstration purposes some of the CC18 I/O will be reflected on the user interface (buttons and LEDs) of an MS120.

Slave program

Here's a program for the slave. Here's what it does:

  • At startup, set the slave's bus address from the two address jumpers.
  • At startup, initiate Xwire communications using a slave configuration table in NVEM0
  • Repeatedly copy 16 inputs into 2 bytes of RAM, where they can be sent via Xwire to the master
  • Repeatedly copy 2 bytes of RAM, which have been "filled" by the master via Xwire, to the 16 outputs.

;=====================================================================
;======== Slave program for making a CC18 into an I/O expander =======
;=====================================================================

;Define RAM locations for the exchange. Here I have used "absolute" (mEQU)

;allocations. I could just as easily use defBYTE allocations, as long as

;I also use defBLOCK to make sure the bytes get allocated to contiguous RAM

;locations.

XwRx0           mEQU            0       ;1st byte from master

XwRx1           mEQU            1       ;2nd byte from master

XwTx0           mEQU            2       ;1st byte to master

XwTx1           mEQU            3       ;2nd byte to master

Start:          XwireGetJumpers         ;Read the 2 address jumpers.

;                                        For address 0 both jumpers should be out

                XwireSetAddr            ;That sets the board address

                XwireSlave      XwTab   ;Start Xwire with its configuration

Loop:           Recall          XwRx0   ;1st byte of data from the master

                LoadX           'FF     ;Enable all bits

                OutputM         0       ;Output all bits

                Recall          XwRx1   ;2nd byte from master

                LoadX           'FF     ;Enable all bits

                OutputM         8       ;Output all bits

                InputFM         0       ;Read lower 8 input bits

                Store           XwTx0   ;1st byte to master

                InputFM         8       ;Read upper 8 input bits

                Store           XwTx1   ;2nd byte to master

                GoTo            Loop    ;Repeat

;-------------------------------------------------------------------

                NVEM0	;Must appear after all executable code!

XwTab:

                NV0Byte                      XwTx0,2,XwRx0,2

; Address of 1st RAM byte to be transmitted ___^   ^    ^  ^

; Number of RAM bytes to be transmitted ___________|    |  |

; Address of 1st RAM byte received from master _________|  |

; Number of receive bytes to expect from master ___________|

;Note: If the byte counts each end do not match, it will not work.

Note: Xwire is not supported during simulation in SPLat/PC. That means it is not possible to be debugging one board and see it interacting with another board while you are using the Run menu. You need to run it in the actual hardware (Translate, Download and then Run button on Module window)

Master program

Here is a fully working program for the master. This will work with the slave program above, that has turned a CC18 into a 16 I/O peripheral expansion board. What the program will do is have the 5 front panel push buttons of the MS120 appear on outputs 0 through 4 of the CC18. At the same time inputs 8-11 of the CC18 will appear on the 4 front panel LEDs on the MS120 (on the MMI202 3 LEDs plus the beeper). I have provided equivalent MMi202 button/LED addresses as well.

I am going to use semaphore addressing in the master. What this means is:

  • Each slave input is read (RecallS) as a semaphore. The semaphore is counted relative to the start of the two byte RAM block that receives the slave input data. The appropriate instruction is  RecallS x,Slv0Inputs, where x is the input number required and Slv0Inputs is the first of the two bytes that receive Xwire data from the slave. You could also use GoIfST ... or GoIfSF ... or WaitForST ... etc
  • Each slave output is written (StoreS) as a semaphore. The semaphore is counted relative to the start of the two byte RAM block that transmits the slave output data. The appropriate instruction is  StoreS x,Slv0Outputs, where x is the input number required. You could also use SetS ... or ClrS ...
What the program has to do is:

  • Get the Xwire communications going.
  • Do whatever it has to do to interact with the slave. In this case it will be simply reflecting slave I/O to the MS120 (or MMi202) front panel. In this simple example I will just do it as a "tight loop" program, with nothing else to do. In a practical program the various accesses to the slave would probably be spread out across several tasks in a MultiTrack program.
  • I will also include a display of the Xwire error counter. This is a very useful tool to use during system test, to monitor that Xwire is working.
Tip: If you are trying to get several slaves going, do one at a time. Write a simple test program that uses the error counter, and extend it as you add each slave to your system. If you try and get several slaves all working at once, you are likely to be overwhelmed if any one of them is incorrect. Divide and conquer!

Note: I am not going  to define named (symbolic) values for all the slave I/O and MS120/MMi202 front panel stuff. I will use simple numeric values. Normally that would be naughty. In this case it is better not to clutter the program with 20+ lines of EQUates.

;=====================================================================

;======== Master program for using a CC18 as an I/O expander =========

;=====================================================================

;Define RAM locations for the exchange.

Slv0Inputs      defBYTE         2    ;Remote inputs stored in 2 bytes as 16 semaphores

                                     ;Each semaphore will correspond to one slave input

Slv0Outputs     defBYTE         2    ;Remote outputs stored in 2 bytes as 16 semaphores

                                     ;Each semaphore will correspond to one slave output

Slv0Addr       EQU             0     ;Define the slave address.

Start:

                XwireMaster     XwTab   ;Start Xwire with its configuration

;For MS120, as I want to use the LCD for error counts, I must turn on the backlight

                On              20      ;MS120 only

;Once the startup stuff is done Xwire will be running

;(if everything is correctly set up and wired together!).

;I now go into a "tight loop" updating I/O and the error count display...

Loop:

;---------- Do the slave inputs: Copy to front panel resources

                RecallS         8,Slv0Inputs    ;Reflecting input 8 on the CC18

;Notice                         ^_________________________________^

                Output          8               ;One front panel LED (Beeper on MMi)

                RecallS         9,Slv0Inputs    ;Reflecting input 9 on the CC18

                Output          9               ;One front panel LED

                RecallS         10,Slv0Inputs   ;Reflecting input 10 on the CC18

;                                ^

;Coincidence ------------------- |

;                                v

                Output          10               ;One front panel LED

                RecallS         11,Slv0Inputs   ;Reflecting input 11 on the CC18

                Output          11              ;One front panel LED

;---------- Do the slave outputs: Set from front panel resources

                Input           12 ;(8 on MMi)  ;One front panel button

                StoreS          0,Slv0Outputs   ;Reflects in slave output 0

;Notice                         ^_________________________________________^

                Input           13 ;(9 on MMi)  ;One front panel button

                StoreS          1,Slv0Outputs   ;Reflects in slave output 1

                Input           14 ;(10 on MMi) ;One front panel button

                StoreS          2,Slv0Outputs   ;Reflects in slave output 2

                Input           15 ;(11 on MMi) ;One front panel button

                StoreS          3,Slv0Outputs   ;Reflects in slave output 3

                Input           16 ;(12 on MMi) ;One front panel button

                StoreS          4,Slv0Outputs   ;Reflects in slave output 4

;-----------  Display the error count. The count displayed will increment

;whenever there is an Xwire error.

fErrors         defFLOAT

                XwireGetErrCount                ;Error count -> X, reset error count

                float                           ;Convert to floating point

                fRecallQ        fErrors         ;Previous running total

                fAdd                            ;Add in new counts, if any

                fStore          fErrors         ;Save

                OBLCD_SetCur     0,0            ;Position LCD cursor

                OBLCD_fDispW    5,0             ;Display running total

;-----------  All done

                GoTo             Loop

;-------------------------------------------------------------------

;The NVEM0 directive must come after all executable code.

                NVEM0

XwTab:

                NV0Byte         Slv0Addr,Slv0Outputs,2,Slv0Inputs,2

                NV0Byte         'FF     ;Sentinel


Xwire: Addressing

In a system with Xwire master and one or more slaves, you need a policy and a technique for allocating addresses to the slaves. In this context the address is what is used by the master to distinguish one slave from another. Every slave must have an address (even if you only have one slave), each slave must have a different address, and the master must be pre-programmed with the correct addresses.

What addresses to allocate

Xwire slaves can be either SPLat controllers (typically a CC18) that you have programmed to be an Xwire slave, or dedicated Xwire peripheral boards that we have made to be Xwire slaves, for example SX10500. Legal Xwire slave addresses are 0 through 253. The master does not have an address. Within that range you can allocate addresses however you see fit consistent with any constraints that may exist within individual slaves (i.e. not all slaves are readily able to take on all addresses).

Xwire peripheral products

If you are using an Xwire peripheral, you should first read up on its documentation to determine available addresses for that particular product. For example, the SX10500 has 8 possible legal addresses, and it has 3 jumpers for selecting one of those addresses.

SPLat controller programmed to be a Xwire slave

If you are using a SPLat controller, like the CC18, you must set its Xwire address using a XwireSetAddr instruction. That instruction takes whatever number is in the X register and makes it the Xwire address of the board.

How that number gets into X is a separate issue. There are several possibilities for the instruction preceding the XwireSetAddr:

  1. You simple use a fixed number with an instruction like LoadX 5:

    LoadX 5

    XwireSetAddr

  2. For the CC18, which has a pair of jumpers, labeled A0 and A1, intended just for this application, you read the jumpers into X using XwireGetJumpers:

    XwireGetJumpers

    XwireSetAddr

  3. You read a number of inputs that have been dedicated to address selection, typically using an InputFM instruction. In this case you would also use an AndM to select the bits being used. For example, say inputs 13 and 14 are used for address selection:

    InputFM 13 ;Read 8 inputs starting at 13

    LoadX %00000011 ;Bit mask to select just inputs 13 and 14

    AndM ;Bit-wise And between the inputs and the mask

    XwireSetAddr

    Note that inputs 13 and 14 are the option jumpers on an MMi202. Note also that they are typically available for use only if there is not a SPice board mounted on the MMi202.

  4. If the slave is a controller with a user interface, like an MS120, you might have a menu item that sets its address. That is definitely an advanced topic and I won't try to expand on it any further here.


Xwire: Some hints and ideas

Despite its apparent simplicity, Xwire is extremely versatile. Here are some more or less random thoughts:

Xwire is a simple exchange of a few bytes between master and slave. What you use those bytes to represent is entirely up to you. The limit is your imagination.

A byte can represent a command to a slave to do something.

Another byte back from the slave can signal completion.

Remember Xwire will transfer data at any random time. If you need to transfer several bytes, and it is important that they be used as a set (say X, Y and Z coordinates), use an extra byte for status. For example, have the status initially 0. The master fills the data bytes. While the master is doing that, any transmissions received in the slave is ignored in your slave program because status=0. When all bytes have been written in the master it sets status<>0. The slave sees that and actions the data bytes. When it has finished it sets a return status byte to something.

You can send large amounts of data. Use a handshake like I outlined above, plus a block counter. Up to 255 blocks of data are easy to transfer.

Data received via Xwire is accurate. We use a 16 bits CRC checksum for error detection. Still, don't use it for a bomb detonator.


Xwire: Error handling

The Xwire protocol is quite robust. It can survive lost messages (packets) and uses a 16-bit CRC to guard against erroneous data being accepted by a receiver. Messages are being constantly transmitted, so if a single message is corrupted the data will get through next time around.

Error handling in Xwire is very simple. The Xwire master maintains an error counter that can be read out using XwireGetErrCount. Reading the counter resets it. It is an 8-bit counter that stops counting when it gets to 255.

Data is transferred between master and slaves by serial data messages (packets, datagrams). When the master sends out a message to a slave, it expects a reply. If the slave fails to decode the message correctly, it ignores it. If the master cannot decode a reply correctly, it ignores it. Whenever the master fails to get a message it expects, it counts up one error. Hence one error count represents a failure somewhere in the round trip.

Message failures can be due to:

  • Excessive wire length degrading the signals.
  • Excessive noise (electrical interference)
  • Mismatched byte counts between master and slave (no messages will make the round trip - this should be detected during system debug!)
  • The slave being temporarily too busy to respond.

It is a good idea, at least during system debug, to arrange to monitor the message error rate. One possibility is a task that reads the error counter every 100mS or 1S or 10S (whatever works) and displays it.

Why would a slave be too busy to respond?

The Xwire system has been deliberately designed to be tolerant of a slave missing messages. We have done it like that to leave ourselves room to design special purpose slave boards that squeeze the last little bit of speed out of their processors.This might be some very high speed peripheral function that fully occupies the processor for periods of time, during which it simply does not have enough grunt to do Xwire communications as well. The master will tolerate periods of no contact with such a slave. The error counter will accumulate counts rapidly but communications with other slaves will barely be affected.

Fatal runtime errors

Certain error conditions will result in a runtime error and reset of the controller. The error codes are documented under runtime error codes.


Xwire: Speed, range and capacities

Speed

Xwire uses serial data at 38,400 bits per second. The time taken to service one slave is approximately 7.2mS plus 261uS per byte of data transferred. For example, if we are sending 10 bytes to the slave and getting 9 bytes back, the total time per exchange will be

7.2mS + (10+9)*0.261mS = 12.16mS

which translates to about 82 updates per second.

Range

The range (maximum cable length) is limited by 2 factors:

  • The capacitance of the connecting cable
  • Electrical noise

Under ideal noise free conditions we have achieved error free board to board transmission with up to 33nF of cable capacitance. That translates to 20m or more of typical screened cable. We have tested an error free connection over 21m of single core screened cable in our office environment. In practice we would not recommend more than 5m of cable end to end. Whether to screen or not depends on length and ambient electrical noise.

Noise is harder to quantify. Consider this: Xwire is intended for interconnecting boards in a known, stable, tested configuration. It is absolutely not intended for machine to machine interconnection in a customer's premises where conditions are unknown. You use it inside a machine, not outside. That means you should be in a position to conduct practical tests while you are prototyping your machine. Write a test program that exercises all the nasty noise generating items inside your box, like relays, VFDs, contactors, inverters, Van den Graff generators, welders, radio transmitters etc. Cycle these things as rapidly as you can without blowing anything up, all the while watching the Xwire error counter. Try to make it error by doing the worst thing you can to it, like wrapping the Xwire wiring twice around a contactor that's switching a 2HP motor. If you can induce errors you will feel more comfortable about it when you "back off" from the point where it starts to play up.

Hint: In extreme cases a noise pulse can reset a processor. This will result in the SPLat program restarting "from the top". Build into your test program something immediately noticeable at startup, like a long beep, or a 10 second pause with no relay activity. That way you will get a clear indication if it does reset.

Number of boards

The number of boards that can be interconnected may depend on the board type. The initial boards containing Xwire (CC18 and MS120) have one kind of processor chip, whereas future boards may have different chips with different drive and loading characteristics. We define the CC18 and MS120 as representing one Unit load and 10 Units drive. The latter means it can drive 10 other boards of the same load rating., so you can have up to 11 boards in a network.

Data capacity

The number of bytes that can be transferred into or out of a board may vary with board type and Firmware revision. Please refer to the Xwire section of each board's documentation.


Xwire: SPLat/PC Xwire window

SPLat/PC 10.23.0 and later have a window that allows you to determine what is returned by XwireGetErrCount , XwireGetPollCnt, and XwireIdlePoll.


Cutting and pasting code from the SPLat Knowledge Base

Throughout the SPLat Knowledge Base (SKB) with have pieces of sample SPLat code. You should be able to copy and paste these into SPLat/PC and run them. Unfortunately, idiosyncrasies in various web browsers and in the HTML help file rendering (which at heart is Internet Explorer) sometimes produce unwanted results like all line breaks being removed.

Here are some things you can try to work around this problem:

I am reading the SKB as:

Possible fixes (we are chasing a possibly moving target here, so these measures may not always work)

A web page, using Internet Explorer as my browser.
  1. Use FireFox or Google Chrome instead.
  2. Copy from the SKB web page, paste into Microsoft Word, then copy from Word and paste into SPLat/PC.
  3. Copy from the SKB web page, paste into a new email in Outlook, Gmail or Thunderbird, then copy back from there and paste into SPLat/PC.
  4. Copy from the SKB webpage, paste into Open Office writer, Save As Text to a file name that you give a .spt extension. Open that file in SPLat/PC.
  5. Using Internet Explorer, enable "Compatibility View", then copy and paste into SPLat/PC

As a splat_knowledge_base.chm (HTML Help) format file (This is where the whole SKB has been downloaded or installed of a CD as a single file)
  1. Copy from the SKB web page, paste into Microsoft Word, then copy from Word and paste into SPLat/PC.
  2. Copy from the SKB web page, paste into a new email in Outlook, Gmail or Thunderbird, then copy back from there and paste into SPLat/PC.
  3. Copy from the SKB web page, paste into Open Office writer, Save As Text to a file name that you give a .spt extension. Open that file in SPLat/PC.


Programming examples

This chapter contains a more or less random collection of program examples that don't fit anywhere else. There are many more examples within individual tutorials. There is also a collection of examples on our website in the File Resources area. Note that some of these are old, and there may be more elegant ways of getting the same result using later features in SPLat.

Previous Topic

Next Topic

In This Chapter

Example: Numeric data entry function with gear shift

Example: Controlling an RC servo

Example: Non-linear math functions using polynomials


Example: Numeric data entry function with gear shift

This programming example addresses the issue of providing a user interface that permits the user to set numeric parameter values. This might be for example a target weight on a batch weighing system or a temperature setpoint on a furnace control. Sometimes such parameters need to be adjustable over a wide range of numeric values, but they must also be constrained within permitted minimum and maximum values. In for instance a batch weighing system permissible values may range from 0.20kg to 20.00kg in steps of 10g (0.01kg).

With controller like the MMi200 we are faced with the restriction that the off the shelf product does not have a numeric keypad. How then do we give a user the ability to enter a number like 19.87kg? One answer would in fact be to interface a numeric keypad, and write a suitable driver. That is perfectly possible, but it is not the solution we will pursue here (the classic tutorial in the SPLat/PC help file has an example of just that).

The solution in this example uses just 4 of the 5 front panel push buttons on the MMi200. They are used (designated) as "Up", "Down", "Yes" and "No" (the latter 2 could also be called "Accept" and "Reject").

What it does is to use the Up and Down buttons to increment or decrement a number with auto-repeating of the button as long as it is held down. However, the amount by which it changes is variable, with an automatic "gear change" feature that takes place while the Up (or Down) button is held down. With the gear change, as a particular digit position has been incremented and rolls over to zero, focus shifts to the next highest position which in turn gets incremented.

For example, assume the number starts at 0 and is initially incremented by one. Providing the Up button is held on, the number will go through the following sequence:

    0,    1,    2,    3, ....    8,    9,
   10,   20,   30,   40, ....   80,   90,
  100,  200,  300,  400, ....  800,  900

etc. If the user releases the button at 900 and then presses it again, the function will shift back down to the lowest gear and resume counting by ones:

  901,  902,  903,  904, ....  908,  909,

  910,  920,  930,  940

etc.

The function allows you to set the initial value, the initial increment and the minimum and maximum allowable values. It actually turns out to be an advantage not having a full keypad, because this method makes it impossible for a user to go outside the min/max limits. With a keypad this becomes much harder.

The example is written as a function that can be incorporated quite readily into a larger program. You can download the examples collection from our website in the File Resources area. The code for this example is called FSETNMVS.SPT. This is an old example, written to be compatible with the Suspend/Resume method of writing multitasking programs, which has been superseded by MultiTrack. The code provided includes test/demo code.


Example: Controlling an RC servo

RC (Radio Control) servos are small devices that position a physical output, usually a rotating shaft, to a particular point under control of a digital pulse width modulated (PWM) input signal. RC servos exist primarily in the hobby sphere.

This example will show you how to control a servo using an output from a SPice connector on a suitably equipped SPLat Controller.

Note: This procedure requires connecting wires directly to the pins of a SPice connector, which have no protection circuitry. Take particular care that none of the wires accidentally touches a terminal it is not supposed to. Always disconnect power before making or breaking connections, and double check wiring before applying power. Keep wires a short as possible and route away from any wires carrying high voltage (120/240V) or current. Any damage to the SPLat attributable to careless practices will void the warranty.

Wiring

An RC servo has 3 wires. These are:

V+ | Positive power. This needs to be wired to a power supply suitable for the servo itself. Typically servo devices require 4.5 to 6V. You need a power supply rated for the current specified by the servo manufacturer. Connect the servo V+ wire to the positive terminal of the power supply (red below).
Common/Gnd/0V/V- | Different servo manufacturers will use different designations. This needs to go to the negative terminal of the servo power supply (above) and also to the 0V pin of the SPLat SPice connector (Green below)
Signal | This is the pin/wire on the servo that receives the pulse width modulated control signal. It needs to be connected to a SPice pin designated as capable of being an analog output (Yellow below)

The diagram above shows a single RC servo connected to SPice pin 8. On an MMi202 this is equivalent to analog output 9 when used with an fAnout instruction.

Programming

Programming is quite simple. It exploits the fact that the "analog" outputs on a SPice connector are actually pulse width modulated 5V signals ... exactly what an RC servo wants. The base frequency of the PWM signal has to be adjusted away from default in order to suit the servo. That means any other analog outputs will suffer, so you can't for example have a 0-10V analog output or thermistor drive at the same time off the same controller.

Here is some sample code: This small program will control 2 RC servos connected to an MMi202 SPice connector.

* Set PWM output repetition frequency to 125 Hz. This gives us a PWM pulse width range of
* 0 - 8ms. The servo used in this example accepts 0.6ms --> 2.4ms.
* Using the floating point Fanout instruction,
* this gives an allowable range of value of 0.6/8.0 = 0.075 --> 2.4/8 = 0.30. As the normalised
* range of 0 --> 1 is realised with a PWM resolution of 10 bits (0 - 1023), this gives us a
* control range of (0.3 - 0.075) * 1024 = 230 counts or 1/230 = 0.43%

        setu            0,7             ;select 125 Hz PWM operation
        SpxCmd1         0,!cpu

        setu            0,0             ;set up SPICE pin 4 as a PWM output
        setu            1,0
        setu            2,0
        setu            3,0
        setu            4,4             ;SPICE pin 4 set up as a PWM output
        setu            5,0             ;SPICE pin 5 is already a PWM output
        spiceconfigu

loopy   floadw          0.075           ;go to one extreme of the travel

        fAnout          8               ;Spice pin 9 on an MMi202

        fAnout          9               ;Spice pin 8 on an MMi202

        pause           200             ;wait...

        floadw          0.30            ;go to the other extreme of the travel

        fanout          8

        fanout          9

        pause           200             ;wait...

        goto            loopy


Example: Non-linear math functions using polynomials

I'll try and keep this as non-mathematical as possible.

For thermistors (specifically) we now have a much easier way.

What this is about is calculating "weird" functions of a single variable, i.e translating one number into another. A common example in my work is linearizing a thermistor. Other examples might be motor speed versus applied voltage, engine torque versus rpm, the flow rate in a pipe versus pressure, etc. What these all have in common is that the "output" (say flow) is not a linear function of the "input" (say voltage). In fact, you may not even have a formula, just a manually plotted curve.

For example, the curve below is a plot of the air flow in kg/hour (Y axis) versus output voltage of a Mass Air Flow (MAF) sensor used in an engine management system. The curve is clearly very non-linear.

Suppose we need to translate analog input readings into engineering units (kg/hr) according to above curve. Here are the steps.

Step 1. Make sure the horizontal axis represents the value read by the SPLat.

This may sound silly, but often the tendency will be to plot the output of a sensor (volts) on the Y axis versus the measured quantity (say flow) on the X-axis. In the procedure we are using we need the data the other way around. What we know when we perform our calculation is the input variable, and it should be plotted on the X (horizontal) axis.

Step 2. Get the data into an Excel spreadsheet and plot it.

Later on we will be using some magic in Excel to do the really hard work. Use Excel's Chart function to plot the data as a scatter diagram. Select the format with a smooth line and no data points. You should get something like I have above.

Step 3. Scale the horizontal (input) axis.

Convert the units on the input axis into the units the SPLat will start out with. Frequently this will be an analog input read using fAnIn, so the input axis should run from 0 to 1. In the current example I will assume the analog input is set for a 0-5V range, so 5V in on the above graph will become 1.0 on the scaled graph, like this:

The only thing that has changed here is the X-axis scale.

Step 4. Find the polynomial

This could be hard, but Excel makes it easy.

  • Click on the chart so the actual curve is selected.
  • In the Chart menu select Add Trendline (this may vary with different versions of Excel. I still happily use Excel 97).
  • In the trendline dialog box, select type = Polynomial and click the Order up to 4 (the latter may change later)

  • In options select Display Equation on Chart

  • In Patterns select the lightest line weight and a contrasting color.

  • Hit OK. You should now have something like this:

  • Can you see the fancy equation? That's what we are after. If you right-click the equation you can change the number formatting via Format Data labels

    A good choice is Scientific with about 7 digits precision:

    Which should look like this:

    (I moved the equation up and compressed the chart area down a bit by clicking on them and dragging the handles).
  • Now move your mouse back over the the curve and shift it around until you get a tool tip that says Series 1 Trendline 1.

    Right click at that point. This will give you a context menu that will let you get back the Format Trendline dialog box with the Type tab. Try changing the Order of the polynomial. Lower orders, say 2, will not produce a trendline that fits the real data curve as well. Higher orders will give a better fit. My version of Excel maxes out at 6th order. The trade off is that a higher order polynomial will take SPLat longer to calculate.

Step 5. Identify the "polynomial coefficients"

The coefficients are the number that appear in front of (as multipliers for) the powers of x.

Here the "4th order coefficient" is 8.6811250E+02. With a little practice you can learn to highlight individual coefficients with your mouse, copy them and paste directly into your SPLat program. Just be aware that Excel adds a space after the minus signs. You must reproduce the minus signs correctly, without a space after them.

Step 6. Use the example code:

Program examples are no longer installed with SPLat/PC. They are now located on our website in the File Resources area. In folder examples\math\poly\ is a program polygen.spt. polygen.spt contains a general purpose polynomial calculator.

The calculator gets its coefficient from a table. All you have to do is build a table of coefficients and point the code to it. The comments in the program show you how to do this.

Other uses

This technique can be used to generate otherwise difficult mathematical functions like trig functions, logs and exponentials. The key to success is to limit the input numbers (arguments) to the narrowest possible range.

  • Only cater for the range of numbers that will actually occur in your application, no more.
  • Use known mathematical relationships like sin(90+x)=sin(90-x) and sin(-x)= -sin(x) (in degrees). With logs you can multiply an argument by a known constant, say 0.1 or 10, take the log and then add the log of the constant.


Support resources

Previous Topic

Next Topic

In This Chapter

SPDownLoad: program downloader

SPLogger: data logging application

reFlash: Updating the firmware in your controllers

PIDassist: PID programming aid, simulator and tutorial

File resources


SPDownLoad: program downloader

SPDownLoad is a utility that allows someone with minimal training to download a translated SPLat program to a controller. It is designed to allow OEM users of SPLat (i.e. someone embedding a SPLat in their own product) to distribute SPLatWare updates to their customers.

SPDownLoad will download translated files with the file extension .b1n (or .B1N). Note that the middle character is a numeral 1. This was selected in preference to .BIN because the latter is quite widely used, so we have tried to avoid conflicts with any other programs. When SPDownLoad is installed it creates an automatic file association with .b1n, so double clicking a .b1n file will launch SPDownLoad.

SPLat/PC versions 7.19.4 and later will generate a .b1n file when you Translate a program. Older versions generate a .BIN file. You can either re-translate or re-name the file.

You can download SPDownLoad from our SPDownLoad support page online.

How to distribute a program update to your customer

You should do several rehearsals of this procedure, including the parts your customer must do, before attempting to teach your customer.

  1. Translate the program and generate a .b1n file.
  2. Locate the file in your computer. The extension will most likely be capitalized.
  3. Either email the program to your customer or make it accessible online.
  4. Get your customer to download and install SPDownLoad.
  5. Instruct your customer how to electrically connect to the SPLat board. This must be done in a safe manner!
  6. Instruct your customer how to drive SPDownLoad. The program has a built in help screen.

Note: We cannot provide support to your customers. You must educate yourself in the process before trying to teach your customer.


SPLogger: data logging application

Previous Topic

Next Topic

In This Chapter

SPLogger: SPLat Data Logging Utility


SPLogger: SPLat Data Logging Utility

SPLogger is a Visual Basic 6 program that allows you to collect data from a running SPLat program and save it to disk as a comma delimited file (*.csv) for later analysis in Excel or other spreadsheet program. You can use SPLogger for intensive and/or long term monitoring of a SPLat controller during development. You can also use it, with a SPLat controller as a "front end", for ad hoc data logging. It can be particularly useful where things are happening on a scale of hours or days, and you don't want to sit watching it with pen and paper in hand. It can also capture events that are happening too fast to capture and record manually (the limit is about 4 changes per second).

SPLogger is provided free on SPLat resource disks (from September 2003). It is also available on our website. The VB6 source files are included as an example of using SPLatLink.

The latest version in V1.3, released June 23 2005. It has a capacity for more memory locations and displays how many records have been logged. The changes are minor and self-evident, so this documentation has not been updated. Floating point only works for addresses <124.

Previous Topic

Next Topic

In This Chapter

SPLogger: Installation

SPLogger: Overview

SPLogger: Operating instructions

SPLogger: Programming the SPLat board


SPLogger: Installation

The SPLogger program is distributed as a single installation file called install.exe. It is distributed on our Resources CD and is also available online in the downloads section of our website. To install SPLogger simply double click the installation file and follow the prompts. The default folder for installation is in a common SPLat folder structure. It is recommended that you stick with that structure as it will impact future consolidation with the SPLat Knowledge Base.

If you want the VB6 source files you can get them off the SPLat resource disk or download them. They appear to work out of the box (it's always hard to know when moving VB6 sources from one machine to another!). SLCom.ocx needs to be installed, but that happens when you install SPLogger. If VB6 complains about the ocx not being present, you may need to manually re-insert it in the toolbox and the SPLogger form. Usually you will first have to get rid of a picturebox called SLCom1.


SPLogger: Overview

SPLogger can monitor and log any combination of RAM locations within the target SPLat board. It will not monitor I/Os or other resources within the board, even though the SPLatLink protocol provides for that.

SPLogger has the following abilities:

  • Log memory data in Decimal, hexadecimal, binary or floating point format. Binary results in 8 columns of 1's and 0's. Floating point extracts 4 bytes of data from the SPLat board. Floating point only works for addresses <124.
  • Trigger a reading (recording of one row of output data) either by internal (to SPLogger) timing or when a selected byte in the board changes.
  • Time stamp each record either with seconds elapsed since the start of recording, with the time of day or with Date plus Time
  • Variable scan time (time between records) from 250mS to 1 day.
  • Live display of all monitored RAM in hexadecimal.
  • Saving of configurations.
  • Undo/redo of configuration changes.


SPLogger: Operating instructions

In the following pages we will go through the various settings, working through the three main panes of the SPLogger display.

Previous Topic

Next Topic

In This Chapter

SPLogger: Setting up what to monitor

SPLogger: Control pane

SPLogger: RAM data display

SPLogger: Status bar


SPLogger: Setting up what to monitor

The top pane of the SPLogger display is where you select which RAM locations you wish to monitor, and what format you want them logged in.

splogger ram selector pane

The pane contains a separate selector button for each RAM location. To set up a RAM location for monitoring you select it, then on the left you chose how you want it logged. The choices are:

Decimal. The data from this location will be recorded as a 3 digit decimal number in the range "000" to "255". We include leading zeros to make the output a bit easier to read.

Hexadecimal. The data from this location will be recorded as a 2 digit hexadecimal number in the range "00" to "FF". We include leading zeros to make the output a bit easier to read. Note that logging in hex format may be of limited use for use in a spreadsheet.

Bin. The data from this location will be recorded as 8 binary numbers "0" or "1". That means it will occupy 8 columns in a spreadsheet. The first column will be bit 7, the last column will be bit 0.

FP. The location is combined with the next three and interpreted as a floating point number. Note that you cannot also log the other bytes on their own. Floating point only works for addresses <124.

None. Turns off logging of the selected byte.

Trigger byte. One byte can be selected as the trigger byte. That means data will be logged only when that byte changes in the SPLat board. You would use that feature in conjunction with a SPLat program that changes the trigger bytes when it wants data to be logged. The alternative is to let SPLogger log data at regular, timed intervals, which is set up from the Control Pane.


SPLogger: Control pane

splogger logging control pane

The Control Pane lets you select how data is to be logged.

Time Stamp lets you specify a time stamp on each record.

Rel gives relative time, which means the time in seconds since logging started. The timer is reset when the program starts, when the program connects to a board, when you clear the log file or when you select a different log file name.

Abs gives absolute time, meaning the time of day.

D/T Gives date and time (not illustrated)

Clr log clears the log file and resets the relative timer. The blue indicator next to this button flashes each time a record is written to disk.

Scan Time (mS) lets you set the frequency at which SPLogger will extract data from the SPLat board. You cannot set this to less than 250mS. The maximum allowable value is 86400000mS, which is 1 day. Note that inevitable errors in Windows timing will lead to some error in the scan time. Typically scan time will be slightly longer (15mS or so) than what you set.

If you are using triggering in the SPLat board (setting up for a change in a RAM location to trigger logging), the data actually logged will be from the scan immediately following the trigger event in the board.

Run/Pause. Clicking the Run/Pause button (it's caption changes with its state) starts and stops logging. Logging is also stopped when you change logging parameters, and must be manually resumed.


SPLogger: RAM data display

splogger ram display

This pane displays all monitored RAM bytes in hexadecimal, including bytes that are part of floating point numbers.

You can click on any one byte and have it displayed in the left hand panel of the status bar in the same format as you have selected for logging (except bytes 2 to 4 of floating point numbers).


SPLogger: Status bar

splogger status bar.gif

The bottom status bar contains several panels. From left to right these are:

Data Monitor. By default this shows the current log record as it is written to disk (or at least as much as will fit). If you click on a byte in the RAM display, it will be displayed in its selected logging format. Click it again to revert to the original display.

Undo counter. This shows how many levels of undo are stored

Log file name.

SLCom status. This shows the state of the connection to the SPLat board. SPLogger automatically scans COM1 through COM4 looking for a SPLat board. You can click on this to isolate SPLogger from the board, and free the serial port. Click it again to re-connect. Isolating is convenient when you are alternating with SPLat/PC to debug a program in the board.


SPLogger: Programming the SPLat board

Programming the SPLat board to work with SPLogger can range from no extra effort, through to a few basic provisions up to creating a moderately sophisticated program.

The minimum consideration you should give to "SPLogging" is to concentrate the data you wish to log into a tight group of RAM locations. The SPLatLink protocol can only extract 16 bytes of contiguous RAM in one message, so the more spread out the RAM locations you are monitoring, the slower it will become.

Here's the quick and dirty SPLat program I used for testing SPLogger in an MMi99 with LCD. It tags all of RAM with its own address. Then it maintains an integer in 1 and a floating point number in 2-5, allowing both to be modified from the MMi99 front panel. Location 0 is used as a trigger by being incremented every second.

      LoadI	128		;Fill all RAM with its own address
Fill
      DecI
      ItoX
      Push
      iStore
      GoIfNZ      Fill
      fLoadW      3.1415926    ;Set a floating point number
      fStore      2

Display

      OBLCD_SetCur  0,0

      Recall      1

      OBLCD_HexDispX          ;Display RAM(1)

      fRecallW    2

      OBLCD_fDispW  9,5       ;Display RAM(2) floating point

Loop

      GoIfInK     12,Up       ;Count up integer?

      GoIfInK     11,Dn       ;Count down integer?

      GoIfInK     9,UpFP      ;Increment FP?

      GoIfInK     8,DnFP      ;Decrement FP?

      Test        0           ;Time for a trigger?

      GoIfT       Loop        ;No

;Trigger

      IncM        0           ;Trigger SPLogger

      SetTimer    0,10

      GoTo        Loop

;Increase FP

UpFP

      fRecallW    2

      fLoadQ      1.1111111

      fMul

      fStore      2

      GoTo        Display

;Decrease FP

DnFP

      fRecallW    2

      fLoadQ      0.9

      fMul

      fStore        2

      GoTo        Display

;Count up integer

Up

      IncM        1

      GoTo        Display

;Count down integer

Dn

      DecM        1

      GoTo        Display


reFlash: Updating the firmware in your controllers

Click here to jump directly to the firmware update page

Previous Topic

Next Topic

In This Chapter

reFlash: What is it?

reFlash: How do I determine what version Firmware is in my controller?

reFlash: How do I learn about an update?

reFlash: Why should I upgrade?

reFlash: Why would I NOT upgrade?

reFlash: Would I ever want to downgrade?

reFlash: What do I need to update a board?

reFlash: How do I update my board?


reFlash: What is it?

reFlash is the ability of a SPLat board to have new operating Firmware loaded into it. The operating firmware is the program that we load into the board, that gives it its SPLat "personality". This is quite different from the program you write to tailor it to your application, which we call SPLatWare.

reFlash is available in 2nd generation boards, starting with the MMi99 with firmware version 3.3 or later (reFlash was developed after the earlier MMi99 versions were released). Older designs, which do not have reFlash, are SP108, SL88, MMi88, AJ18, PMC3, CA20, OEM32 and OEM36.

There are three reasons why you would ever want to reFlash a SPLat board:

  1. Because we introduce new firmware features quite frequently, it has become impossible to ensure that all our (and our dealers') inventory is fully up to date. Therefore it is possible that a board you receive does not contain the "latest and greatest". Thanks to the miracle of reFlash technology you can still update your board to the latest version.
  2. We goofed, and there's a bug in the existing firmware. For example a SPLat instruction giving incorrect results.
  3. We devised a new feature (for example new instructions) and you'd like to upgrade your existing board(s). This makes most sense if you keep a board for development. There is little point in upgrading boards already shipped in your product, unless you are also upgrading the SPLatWare and you have used new language features.


reFlash: How do I determine what version Firmware is in my controller?

When you connect to a controller from SPLat/PC, using the Connect button in the Module window, the Firmware version is displayed in the title bar of the Module window.


reFlash: How do I learn about an update?

The best way to discover if a new version of Firmware is available for your board is via the latest version of the SPLat Knowledge Base published on our website. Firmware release information is located within each board's product documentation.


reFlash: Why should I upgrade?

There are two primary reasons to upgrade to a new Firmware version:

  1. To fix a known bug in a feature you are using.
  2. To get a new feature you need.


reFlash: Why would I NOT upgrade?

There could be times when you elect not to upgrade your Firmware.

  1. What you have is working perfectly fine in your application. There are no known bugs and you don't need any of the newer features offered by later firmware.
  2. This one is a bit more subtle: Imagine your program is very large, and is pushing the limits of available program memory space. Because new firmware features take up more memory space, it is possible for a larger firmware to take away from available user program space. Therefore, a firmware upgrade could conceivable "break" your program. But fear not, you can always "downgrade" the firmware again to a version that has worked for you.


reFlash: Would I ever want to downgrade?

There are two scenarios where you might actually want to downgrade to a version of Firmware that pre-dates what shipped in your board.

  1. We goofed and released an "improved" firmware that inadvertently introduced a bug. Rest assured, we test every release very thoroughly, but we are also human. Naturally we will fix the bug and release an upgrade very quickly. Meanwhile, the quick work-around will be to downgrade the firmware in your board to a known good version.
  2. (The probability of this one is very low, so don't stress over it!). Your application program is very large and takes up all the available program memory in the version of firmware you developed it on. Then you get shipped a batch of controller boards with "new improved" firmware that takes up more memory. Suddenly your application won't load into the boards because the amount of available program memory is less (firmware and your SPLatWare both use slices out of the same memory area). The solution for you is to reFlash your boards back to the version of firmware you had when you developed your program. You can also order boards with a given firmware version, though an additional charge and/or minimum order quantity may apply.


reFlash: What do I need to update a board?

To update the operating Firmware in a SPLat board via reFlash, you need a number of things:

  1. The board, which must be one that can be reFlashed. When you connect to a board in the SPLat/PC Module window, if the indicator [R] is displayed then the board is reFlashable. This applies to SPLat/PC version 6.10.1 or later (earlier versions cannot detect reFlashable boards). The individual product documentation for the board will also reveal if it is reFlashable.
  2. The updated firmware. This is in a file with the .rfl extension, and a filename that encodes the board type and firmware revision, for example "MMi99V3-7.rfl". Firmware upgrade files are available on our website. To access a file locate the "Firmware versions history" section for your board in the SPLat Knowledge Base (SKB). Preferably use the online version, as it is the most up to date. In that section you will find a table of versions with hotlinks to the files. You will also find the files in the reFlash folder on the resources CD shipped with all SPLat controllers in the Developer's Kit (DK) package.
  3. The reFlash utility program installed in your computer. You can find it in the downloads section of our website. You will also find it in the reFlash folder on the resources CD shipped with all SPLat controllers in the Developer's Kit (DK) package.
  4. A regular connection cable and a regular power supply for the board, just like you use for SPLatWare development.


reFlash: How do I update my board?

To update a board with reFlash, make sure first that you have all the things you need, then:

  1. Turn off power to the board (the process may fail if you don't do this)
  2. Physically connect the board to your computer and power it up.
  3. Launch the reFLash program. The reFlash program contains its own built in instruction (Help) screen. When you have studied that screen you can close it to expose the main control screen. You can always get the help screen back by clicking the help button. (You can also double-click the rfl file. When reFlash is installed it sets up the association between *.rfl and reFlash. You can then skip step 4)
  4. Connect to the board by selecting the correct COM port. The board details should be displayed, including the board type (as a hexadecimal code) and the current Firmware revision in the board.
  5. Browse to the rfl file. When you have selected the file its information should be displayed.
  6. Very carefully confirm that the board types for the board itself and the rfl file match. Do not continue if they mismatch, unless you know absolutely for certain that there is a good reason for the mismatch (typically this would be if the board is to be reprogrammed for a totally different function, say converting a regular SPLat controller into a non-SPLat, special purpose function). From a reFlash point of view, board upgrades involving a different type number make them DIFFERENT. For example, reFlashing an MS12 with MS120 firmware will damage the board.
  7. Confirm that the firmware revision of the rfl file is an advance on what's already in the board. Carefully check the revision history and any notes that are associated with the rfl file.
  8. When you are absolutely sure you wish to proceed, click the red BURN button. There is no going back once you do this.
  9. The reFlash process will take a short while. When it is completed the board information display should match the rfl file information.

If anything should go wrong during the "burn", say the power fails, you can simply repeat the process. There is a small program inside the CPU chip, called a loader, that controls the reFlash process, and never gets erased.

Note: Once the reFlash utility is installed you should be able to double click the rfl file and the program will launch itself.

If the board contains a program that uses the serial port, for example for ModBus or SimpleHMI, then it will only be able to connected to the reFlasher for 10 seconds (typically) after power-up. After that time the serial port will become deaf to the reFlash protocol.

Once a board has been connected to the reFlash program, it must be powered off and on again before it will connect to SPLat/PC. reFlashing will delete any SPLatWare program in the board and replace it with a "null" program, so you must download your SPLatWare after a reFlash.

Note regarding custom SPLat boards

With custom SPLat boards that we may supply a rfl file that includes the SPLatWare, so a field upgrade can be performed in one operation. Check the notes that apply to the particular rfl file.

Production mode

The reFlash utility V3.1 has a new production mode. This simply bypasses the initial warning and information screens and "gets on with the job". To invoke production mode:

  1. Launch the program.
  2. Click past the warning and information screens
  3. Hold down CTRL+SHIFT and click the Help button.
  4. Close the program.

When you launch the program again it will be in production mode. Repeat the procedure to turn off production mode.


PIDassist: PID programming aid, simulator and tutorial

PID control is a well-accepted technique for applying closed loop control to continuous processes. It is used in everything from car cruise controls to petroleum refineries. SPLat allows you to incorporate PID control into your product easily and inexpensively.

We have developed a comprehensive toolset to make PID as easy as possible for you.

pidassist composite screen shot

PIDassist is a multi-purpose Windows program. It combines the functions of a process simulator, a simulated controller, a data logger and an automatic code generator to provide you with a comprehensive toolset for developing PID control applications.

Just as important is the comprehensive PID tutorial that is included in the SPLat Knowledge Base. It explains the basic concepts in a clear, intuitive and non-mathematical manner, then guides you step by step through the complete process of characterizing the process you want to control, tuning the control loop and implementing the SPLat program.

You can download the PIDassist package free from the downloads section of our website.

The PID tutorial starts here.


File resources

Our online file resources contain a selection of useful files relating to SPLat. This is where you will find mechanical outlines and the like. The content will change from time to time. Files are supplied "as is", but genuine customers are encouraged to contact us to request other related materials. We will accommodate your requests as far as we can.

Note: We have abandoned the older ftp site and moved to http.


Newsletters (most recent first)

SPLat Happenings was an early newsletter that we publish at highly irregular intervals. It contains product news, technical tips and sundry trivia. We try and make it interesting and useful, so it's not just company propaganda.

Early issues of SPLat Happenings were printed and mailed. The first several issues are available online as pdf files on our newsletter page. In September 2002 we have switched completely to publishing online within the SPLat Knowledge Base.

July 2009: SPLat Happenings has been replaced by a new format newsletter, Control Matters, and is archived and managed online.

Previous Topic

Next Topic

In This Chapter

SPLat Happenings newsletter February 2006

SPLat Happenings newsletter December 2004

SPLat Happenings newsletter December 2003

SPLat Happenings Newsletter October 2003

SPLat Happenings Newsletter May 2003

SPLat Happenings Newsletter December 2002

SPLat Happenings Newsletter October 2002

SPLat Happenings Newsletter September 2002


SPLat Happenings newsletter February 2006

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions.

We have just completed the largest ever single upgrade to SPLat's capabilities, so this newsletter is dedicated to telling you about all the exciting new capabilities.

Previous Topic

Next Topic

In This Chapter

MultiTrack: Multitasking made (ridiculously) easy

MMi200 becomes MMi201

Real Time Clock

New memory mode (NVEM)

Timers cover 10mS to 46hours in a single leap.

MiniTutes get you started quickly

More pulse counting capability

Reminder: Boston training session


MultiTrack: Multitasking made (ridiculously) easy

MultiTrack is our new multitasking system. MultiTrack makes it easy to write programs that do many things at once. With MultiTrack we are confident in claiming SPLat is the easiest controller in the world to program for the majority of machine control applications - easier than C, easier than Ladder, easier than Basic.

To write MultiTrack you need to learn exactly two new instructions. That means with MultiTrack and FastTrack just 16 instructions could be all you ever need to learn to write timing and sequencing programs that do several things at once. Naturally, there's a tutorial to make it really easy to learn.

MultiTrack will be made available on all current controllers, i.e. MMi201, SL99 and MS12.

As a bonus, MultiTrack provides a perfect framework for libraries of common functions. In future you can expect to see us release sample programs for a range of common functions. The first is a sophisticated user interface for the new Real Time Clock.

=========== Comparing SPLat with MultiTrack to other programming paradigms. =============

Ladder logic

Ladder logic is inherently multitasking. That's the good news. The bad news is that unless you have been trained to think in ladder logic it is a paradigm that very effectively masks the logic of any program. Ladder is particularly challenged to handle sequential functions and processes, which are the life blood of most machine controls.

C

C is a very powerful language. For computing (creating word processors, operating systems and other PC applications) C is the language of choice for very many professional programmers. However, controller applications are not computing. Different things are important in controllers. Even if you can learn C (not exactly easy!), you must also buy, learn and understand an operating system.

Basic

Basic is seductive. There are even controller product that use "Basic" in their name. However, Basic was invented to teach college students simple computing. It was not designed for control applications. Incidentally, we use Visual Basic for SPLat/PC and our support programs like SPLogger and PIDAssist, so we know it well. Some versions of Basic found in controller product have built in multitasking. None of those products comes in a properly industrialized form like SPLat, with protected inputs and decent outputs.

SPLat

The SPLat language was designed from Day One for machine control applications. It has evolved over the last 10 years in directions that have been guided by real user feedback. We include the language features that will make controller programming easy, like an instruction to wait for an off to on transition on an input (at best several lines of code in C or Basic and several rungs in ladder). SPLat handles event triggered and timed sequences with consummate easy. MultiTrack makes multitasking very, very easy to achieve as a simple, built in feature of the language.

Even if you have never written a program in your life, with our easy programming methods and our mini-tutorials, providing you have basic computer operating skills and have a need for a control system, you can start to see how it can all work within one hour or two hours of installing and running our free programming software, SPLat/PC. You can download the latest SPLat/PC here.


MMi200 becomes MMi201

The MMi200 controller has been superseded by MMi201. The main difference is that the MMi201 has an optional Real Time Clock. We have also added a Request To Send line to the serial interface, in anticipation of new communications capabilities.


Real Time Clock

We released the new MMi201 controller in December. This follows on from the MMi200, and is essentially an MMi200 with the capacity for an optional Real Time Clock (RTC). The RTC provides time, date and 16 trigger events that can be set to go off any time of day on any combination of days of the week. Naturally the RTC is supported by a rich set of instructions and a tutorial.


New memory mode (NVEM)

NVEM stands for Non Volatile Extended Memory. NVEM is suited to lookup tables, storing repeatedly used text strings and other more esoteric uses. It has a "write infrequently, read often" capability. NVEM user's guide.


Timers cover 10mS to 46hours in a single leap.

Along with MultiTrack we have introduced a new SuperTimer feature. This gives you a mechanism that can generate intervals from 10mS to 46 hours in a single mechanism. The classic instructions like Pause have had their range extended, and there are also a number of new instructions that take advantage of this new capability.


MiniTutes get you started quickly

SPLat/PC, our programming software, now sports Mini-Tutorials, or simply MiniTutes. These are short, interactive lessons covering some particular aspect of SPLat. They are particularly useful for people who are just starting out with SPLat and/or controls in general, as they introduce key concepts in easily digested bytes.


More pulse counting capability

All current standard controllers now include a quadrature counting capability. This is what you need to count pulses from bidirectional incremental encoders, that is rotary encoders that have 2-channel output (normally labelled A and B). Documentation.


Reminder: Boston training session

Just a reminder about the training in Boston March 1-3. There are still some vacancies, but hurry! I have extended the enrollment deadline to February 21st, a day or two before I get on a plane for America. For the course fee you get to keep the trainer board, which is loaded with interesting things to "play" with. More details on our website.


SPLat Happenings newsletter December 2004

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions.

2004 has been a busy and exciting year at SPLat Controls. So busy in fact that this is the first newsletter I've found time to write. Not that it's any less busy right now ... if anything it's more busy with the inevitable "must have it before Christmas" panics.

Highlights of the year include:

  • An exciting new controller with built in operator interface, the MS12. A kind of budget MMi99
  • The MMi99 upgraded and renamed MMi200, with a huge increase in memory and capacity for exciting new features.
  • A number of new SPice add-on boards ranging from embedded webserver support to speed control of small DC motors.
  • Visits to the USA and India, and appointment of representatives in both countries.
  • Several new custom SPLat controllers defined, designed and prototyped and moving towards volume production.

I wish you all the best for the festive season, and a prosperous new year!

Previous Topic

Next Topic

In This Chapter

HMI controller breaks $100 barrier

A feast of SPice!

MMi200, son of MMi99, grandson of MMi88

SPLat reps in Boston, Bangalore


HMI controller breaks $100 barrier

The MS12 is a complete controller with 12 digital I/Os and an LCD-based operator interface. It was inspired by the ever-successful MMi99 and its predecessor the MMi88. The MMi88 was the first user programmable OEM controller in the world with a built in operator interface, and the MS12 carries on that tradition of innovation.

Click here for full information


A feast of SPice!

This year, especially the last 4-5 months has been a very busy time in our design department, cooking up all kinds of Touch of SPice add-on boards.

The Touch of SPice concept is very simple but extremely effective: To a standard controller, with its generic facilities, add a small add-on daughter board that provides a "Touch of SPice", some special functionality that would otherwise be very hard, very expensive or downright impossible to source.

The new SPice boards introduced this year include (click the links for full information):

Embedded webserver support. Your SPLat based product can now have its own presence on the world wide web.

6-channel water level switching using conductivity probes. This one is a big hit with water pumping and liquid food processing applications.

3-phase AC voltage measurement. Ideal for genset and energy management products.

K-type thermocouple. For low temperatures our very low cost thermistor temperature measurement is still the least expensive, but this board will take you up to 1024°C in 0.25°C steps.

Small DC motor speed control. Ideal for small permanent magnet 24VDC motors in applications like peristaltic dosing pumps and small robotic mechanisms.

Analog I/O expansion. Add more general purpose analog I/O to MMi200 and SL99, or give the MS12 some analog capability.

These are small, low cost add-on boards performing functions you might otherwise have to pay big bucks for as an external "box". Also, what doesn't show up in this list is the very unique functions we can and do brew up as custom SPice boards. These are typically "weirdo" circuits needed to interface some proprietary sensor to a controller. You won't find our competitors offering them because they are "non-standard" and the quantities are low (i.e. less than 10,000). We can do a batch of custom SPice boards as small as 15 to 20 pieces and be cost effective.


MMi200, son of MMi99, grandson of MMi88

In 1998 we introduced an innovative new product: The MMi88 was the world's first user-programmable controller with a built in operator interface and user-customizable graphics overlay. Since then the product family has evolved with an LCD and many more onboard features. The latest version, released this year, is the MMi200.

The most important enhancement in the MMi200 is a much larger program memory (25K vs 9K) to accommodate more complex programs and text strings for the LCD. Starting late November we are also shipping the LCD version (MMi200DK216) with a blue backlit LCD. Less immediately visible, but equally important, the larger processor we are using gives us room to add new Firmware features in the future. Watch this space!


SPLat reps in Boston, Bangalore

In January I visited Chicago for National Manufacturing Week, and also Boston. As a result of that visit Integrity Engineering Services (IES) in Winchester, MA have been appointed as our representative for New England and upstate New York. IES's Fred Pacilli has a huge amount of experience in sensors, control systems design and trouble shooting, including safety related failure analysis in the nuclear power industry and vendor QA in the military. IES are providing pre-and after sales support, including system configuration assistance and application programming. Phone 781-729-3005.

In September I visited India and selected Vara Associates as our primary agent for that country. Vara Associates are headquartered in Bangalore and work via a network of associates across India. Proprietor Ramdas Chandrashekar holds a Masters degree in Industrial Engineering from Texas A&M University, specializing in engineering management and systems simulation, and has considerable prior experience in board level controller products. Phone +91-98457-73556


SPLat Happenings newsletter December 2003

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions.

The year is drawing to a close and here in southern Australia the heat of summer is starting to be bite. Last night Melbourne sweltered in a minimum overnight temperature of 26°C (82°F), but I reckon that's easily preferable to shoveling snow like some of my friends in the northern USA and in Canada!

We've had a busy year here at SPLat, with several new expansion boards added to the range, and quite a few new "embedded" features in our controllers. There have also been several custom SPLat controllers designed which I can't brag about publicly. In addition to that we have added considerably to the store of SPLat support resources, with such things as the SPLogger data logging program and our new tutorial on multitasking. Our ongoing aim is to empower you, our users to make better control systems for your products, not only through the products we sell but also by relevant instructional materials and other support resources. For the new year there's an exiting new controller at the "white-board" stage, an expansion board with 160A switching capacity and some very neat new firmware functions that will be done as soon as we can find the time.

I hope your year has been as productive and rewarding as ours, and wish you all the very best for the coming year.

Previous Topic

Next Topic

In This Chapter

Competition: SPot the SPLantas

Tech tip: Coils ain't coils

New product: Push buttons for SL99 & MMi99

New product: SPice board measures AC current

SPLogger data logging application

Ascii schematic editor


Competition: SPot the SPLantas

SPLanta 100 wide It's the festive season once more, and time to run our annual SPot the SPLanta competition. This is the 3rd or 4th year we've run this, so it's becoming a bit of of a tradition. Be the one to correctly count the number of SPLantas hiding on our website and you could win.

I have an MMi99DK216 to give away to one lucky person who can correctly count the number of SPLantas.

To enter you should do the following:

  1. Visit the SPLat Website and locate all the SPLantas. Do not include the two in this newsletter or in any other newsletters!
  2. Copy and paste the URL (page address) for each page with a SPLanta into an email
  3. Send the email to [email address deleted due to spam] including your name and delivery address
  4. Start hoping

Entries close January 17th 2004. A random selection will be made from the correct entries. Employees of SPLat Controls or associated sales representatives may not compete.

The winner will be announced in the next newsletter.

PS: We will pay airmail delivery but all taxes and duties in your country will be your responsibility.


Tech tip: Coils ain't coils

I had a call the other day from a customer who couldn't work out why his MMi99 outputs blew up when driving a 24V 500mA water valve coil. I've seen this kind of thing before (there's not much I haven't seen in the "traps for young players" department), so I asked him exactly what the printed ratings were on the coil. Here are the ratings:

24V - 12VA - 2W - 60Hz

Ahah! If the coil has a VA rating or a frequency then it's an AC coil. Coils ain't coils, and a coil designed for AC is going to draw huge currents if run on DC. Other clues would have been "24VAC" or "PF=0.4", i.e. anything else indicative of AC operation. Of course, if it had said "24VDC" then I'd have to look elsewhere.

Now a coil can be considered to consist of a pure inductor L in series with a resistor R:

        R      L

       ___    ___

    o-|___|---UUU---o

If an AC voltage is applied across the coil, the current will be limited by the inductive reactance 2*Pi*f*L in series with the resistance R. If a DC voltage is applied, only the resistance will affect the steady state current.

Let's look at the ratings 24V, 12VA, 2W, 60Hz.

From those figure I deduce that on 60Hz AC the coil will draw 12VA. VA means volt-amps, the product of voltage and current. The 2W means it will dissipate 2W of power (resulting in heat). The ratio 2W/12VA is called power factor but that's incidental to this discussion.

Now, if we have 12VA and 24V then the current must be 0.5A because 24V * 0.5A = 12VA. We also know from the nameplate information that with 0.5A through it the coil will dissipate 2W of power. If we invoke the equation P=I*I*R we can calculate R=2/(0.5*0.5) = 8ohms.

If an 8ohm load is driven from 24VDC the current will be 24V/8ohm = 3A. That is why it was blowing up a SPLat output that's rated 0.5A maximum! Clearly it is the inductance that is mainly responsible for limiting the current when AC is applied. And by the way, if the coil were left with 24VDC across it it would get pretty hot. It has been designed to dissipate 2W but with DC it would be dissipating 24V * 3A = 72W, 36 times what it was designed for.

If you are in the slightest doubt about a coil or other wound component (say a motor), then measure it's DC resistance using a multimeter and calculate its current draw from Ohm's law.

And remember: Coils ain't coils!


New product: Push buttons for SL99 & MMi99

An OEM customer recently asked if it would be possible to add 4 more push buttons under the LCD of MMi99s. Thankfully that sort of thing is quite easy to cobble together as a SPice board, so that's what we did. The result is the SPice10203, a simple little board that connects to an MMi99 or SL99 via a flying lead.

sp10203 general view


New product: SPice board measures AC current

There are many real-world situation where you may want to measure a 50/60Hz AC current. Typical uses might be motor current monitoring and general fault sensing.

The new SPice10204 provides such AC current measurement easily and economically. It is a SPice board for MMi99 and SL99 controllers. It consists of a small printed circuit board with a miniature current transformer, a precision rectifier and a DC amplifier. The full scale range is 5A rms. The circuit is average responding but scaled for rms current with a sinusoidal waveform (which is to say it responds just like a typical non-rms responding multimeter).

As a "bonus" the board has 2 potentiometers that can be used for any arbitrary purpose, such as SPLatWare based calibration or analog settings.

More details on our website.


SPLogger data logging application

When you are developing a new product with an embedded controller, it can often be a matter of researching the underlying process (i.e. how to make the product work best) at the same time as you are developing the actual control system (that will embody that research). In such cases have a data logging facility can be extremely useful. Data logging means you can record events and readings on your computer for later analysis, using perhaps an Excel spreadsheet.

An example of this would be the development of an HVAC controller. Things can happen slowly in air conditioning, on a scale of hours, days or even, in the case of serious optimisation, months. Being able to log major parameters plus the actions of the control system for later analysis can provide significant benefits. Making such data logging a function of the controller has obvious benefits of easy access to controller information and less money spent on equipment.

This is why we have developed a data logging program to go with SPLat. We call it SPLogger.

SPLogger is a program that runs in a PC and extracts data from a connected SPLat controller. It can be set to record readings of any combination of selected RAM locations (though currently only out of the first 128 locations ... we expanded RAM to 196 bytes after SPLogger was written). Readings can be done on a fixed time basis (every so often) or they can be triggered by an event in the SPLat controller. Readings can be recorded in decimal (1 byte), hexadecimal (1 byte), binary (1 byte) or floating point (4 bytes). Readings can be time stamped with elapsed time or time of day. The output is a comma delimited file (*.csv) that can be loaded directly into Excel.

SPLogger is free and available in the downloads section of our website. It is also being distributed on the resources CD supplied with all SPLat controller Developer's Kits. As an added bonus we supply the Visual Basic source code, so you have a fully worked example of our SPLatLink protocol and SLCom ActiveX control in action.


Ascii schematic editor

When I am wearing my tech support hat, one of the tools I find useful is AACircuit by Andreas Weber. AACircuit is a schematic editor program that lets you draw simple electronic schematics using only printable characters. That means you can send them by email without messy attachments, worrying about file formats etc. The program has a library of the most commonly used electronic circuit symbols, and you can add your own. The only requirement for viewing the schematics is that the email reader be set to a fixed pitch font such as Courier New. Here's an example of AACircuit's output:

           TTL to MMi99 input interface

                       BC547            MMi99 I/P

                 10K         --------------------

 TTL input       ___       |/

    ------------|___|------|

                           |>

                             |

GND                          |           MMi99 0V

    -------------------------o------------------

AAcircuit is freeware. You can download it from http://www.tech-chat.de/


SPLat Happenings Newsletter October 2003

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions at the end.

Spring is in the air down here in Australia. Here in Melbourne that means the blossom trees are in their full glory, the hay fever sufferers are sneezing and snorting, and we are having those sorts of days where it's heater in the morning and air conditioner in the afternoon. At the end of the month we go onto daylight savings, about when the US comes off it. I welcome that, because it reduces the time difference by 2 full hours. Seeing that I seem to spend more time on the 'phone to the US than locally, talking to our many American customers, that translates into more sleep for me. Roll on summer!

In this issue

RAM and subroutine stack increased 50%.

SPLat Happenings newsletter December 2003

Profile of a custom SPLat: JM01

Tutorial: PID control without the math

Programming: SPLatty's repertoire expands

Technical: Resolution and accuracy are not the same thing!

Name change


RAM and subroutine stack increased 50%.

The task of allocating resources within the microcontroller chips we use in SPLat is a never-ending battle. On one hand we want to make as much RAM, Flash memory etc. as possible available to you to use for your programming. On the other hand we have plans and hopes for future features that may also require those resources.

However, we have been able to identify some extra RAM that we can make available to users. SL99, MMi99 and certain custom controllers will from the next (dialect 12) Firmware upgrade provide 192 bytes of user RAM, a 50% increase over the current 128 bytes (and a 6-fold increase over the first SPLat).

We have also allocated two more levels of subroutine stack, meaning you can now stack subroutines 6 deep rather than 4 deep.

These changes both reflect the fact that SPLat is being used for increasingly sophisticated control applications. For example, dialect 12 introduces two very powerful instructions for multitasking support, These require the use of 2 bytes of RAM for every task and will tend also to lead to deeper subroutine stacking.

As an offsetting factor, the introduction of semaphore (bit manipulation) instructions will reduce the amount of RAM used in many applications.

All SL99s and MMi99s with firmware V3.3 or later can be reFlashed to the new dialect. Please contact us regarding upgrading custom controllers.


SPLat Happenings newsletter December 2003

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions.

The year is drawing to a close and here in southern Australia the heat of summer is starting to be bite. Last night Melbourne sweltered in a minimum overnight temperature of 26°C (82°F), but I reckon that's easily preferable to shoveling snow like some of my friends in the northern USA and in Canada!

We've had a busy year here at SPLat, with several new expansion boards added to the range, and quite a few new "embedded" features in our controllers. There have also been several custom SPLat controllers designed which I can't brag about publicly. In addition to that we have added considerably to the store of SPLat support resources, with such things as the SPLogger data logging program and our new tutorial on multitasking. Our ongoing aim is to empower you, our users to make better control systems for your products, not only through the products we sell but also by relevant instructional materials and other support resources. For the new year there's an exiting new controller at the "white-board" stage, an expansion board with 160A switching capacity and some very neat new firmware functions that will be done as soon as we can find the time.

I hope your year has been as productive and rewarding as ours, and wish you all the very best for the coming year.


Profile of a custom SPLat: JM01

Making custom controllers is what sets SPLat Controls apart from all other makers of board level programmable controller products. Usually have to keep our custom designs confidential, so we can't brag about them. Just recently, however, we completed a design for a customer who had no hesitation giving us permission to publicise it. The customer is John March & Co, a Melbourne electrical contractor who provide control panels to a major Australian supplier of high speed industrial roller shutter doors.

Until recent times John March were using regular industrial PLCs in their panels. Then their customer decided to switch from a system using cams and microswitches for position sensing to a proprietary rotary encoder. The encoder has an RS485 interface and "talks" a completely proprietary protocol. There is no way an interface for such an encoder could be bought off the shelf.

The door systems also use a special detector in the rubber edge seal that detects if the door is obstructed. This detector has a unique fail-safe interface that generates a 2kHz squarewave when clear but nothing when obstructed. Again, there is no simple way of interfacing such a detector to a regular PLC.

SPLat to the rescue!

The SPLat controller we designed for John March is called JM01. It has the following features:

  • RS485 interface and custom protocol handler for the rotary encoder. To the SPLat program the door position is simply read in as a floating point number.
  • Custom interface for the "edge detector"
  • A connector for a remote control radio receiver.
  • A connector for a loop (vehicle) detector. In the future we may design our own loop detector module.
  • 2 relay outputs for driving contactors.
  • A special isolated output and a matching input to allow two controllers to be interlocked with each other and other equipment.
  • Several general purpose digital I/O points.
  • A separate board with LCD display, 4 push buttons and 4 LEDs.
  • Powered off 24VAC

The picture shows the JM01 on my desk complete with my test setup (I wrote the SPLatWare application program for it, so I needed a convenient test bed).

You can see the controller in the front. The operator panel is connected with a simple ribbon cable that can be up to 600mm long (we have done other custom controllers where the operator interface can be many meters away, but in this case the two are always in the same cubicle).

Behind it is the test box. This contains switches to simulate inputs and LEDs to display outputs. There's a bit of circuitry in the box, for example a widget to simulate the edge detector. The test box doubles for production testing.

On the left you can see what I did to save having a 1HP motor and variable speed drive on my desk. The square metal object is a stepper motor out of an old floppy disk drive. It is coupled to the black encoder just behind it using Blue-tack (an indispensable Engineering Material!)

The stepper motor is driven by an old SPLat MMi88 (the predecessor to MMi99) I just happened to have lying around (top lefthand corner of the picture, behind the encoder). It contains a simple program that generates fast and slow forward and reverse stepping sequences. This very nicely emulates the variable speed drive used in the Real Thing. Front panel buttons allow the encoder to be jogged to any position, and can also emulate faults by inhibiting movement. I didn't bother with reproducing acceleration.

The SPLat program in the JM01 controller has an interesting assortment of features:

  • A comprehensive menu system for setting up the controller. It has 3 access levels: Factory, Service and User. Access to the Factory and Service levels is controlled by secret key sequences.
  • A learn mode for installation, where the unit is taught the open and closed encoder readings, plus intermediate points for speed changes.
  • Cycle counters for service intervals and total.
  • Automatic and manual modes, plus a special refrigeration cycle.
  • Detection of encoder fault.
  • Diagnostic displays that will help service agents identify problems over the 'phone to a customer.


Tutorial: PID control without the math

PID stands for "Proportional, Integral, Derivative". It is a mathematical way of describing a closed loop control method that is very widely used throughout the process industries. Closed loop control is about controlling, for example, a temperature by measuring it and then applying a correction by adjusting the amount of heat being Closed loop control in-principle diagram provided. PID is only one possible form of closed loop control, but it is very popular because it can be very effective.

I've often been asked if SPLat "has" PID control. My answer has always been something like "Well, yes, you can do PID with SPLat because it has floating point." However, to implement a PID system from scratch can be a daunting task.

We realise that many of our users are not controls experts or PID experts. In the main, your expertise lies in other areas like machine design or product development. That's why we work so hard to make the stuff we regard as our fields of expertise as accessible as possible to others.

To help you implement PID controls within SPLat we have developed a complete PID toolset. This consists of a tutorial, a software modeling and simulation tool and various support materials. The whole toolset is completely free.

Tutorial

The PID tutorial assumes no prior knowledge of closed loop control, and avoids the mathematics. Nevertheless it seeks to give you a good understanding, rather than just have you take things on blind trust. It is important that you have some understanding so you can handle problems that arise in an informed manner.

PIDassist program

PIDassist is a Windows program that provides multiple functions:

  • A data logger that lets a SPLat controller be used to measure and record the process you are seeking to control.
  • A process simulator
  • A simulated SPLat controller
  • An automatic SPLat code generator

PIDassist can be downloaded free from the downloads section of our website.

Support materials

The package includes a number of spreadsheets that are used while determining the characteristics of your target process. These are included in the PIDassist download. There are also several SPLat programs that help you during various stages of your development.


Programming: SPLatty's repertoire expands

This newsletter coincides with the release of dialect 12 of the SPLat language.

Dialect 12 adds some extremely useful instructions. These fall in 3 major groups:

Semaphores

There are 11 instruction in this group. They allow very easy use of single-bit logical variables, which we call semaphores. Alternative names for semaphores are flags or switches, or if your background is with relay logic, internal relays. All of RAM can be used for semaphores. That would mean 1024 possible semaphores in 128 bytes of RAM. However, we have also increased the available user RAM to 192 bytes, so you could potentially have 1536 semaphores.

There is a new tutorial devoted to semaphores.

Multitasking support

These are just two instructions, but they will have a profound effect on advanced programmers using SPLat. The instructions are called Suspend and Resume. They allow you to Suspend the execution of a subroutine and remember ("bookmark") where you left off. Later, when you call the subroutine again, you can Resume where you left off. This makes it very much easier to write multitasking applications with SPLat. A multitasking program is one that appears to be performing 2, 3 or more separate tasks simultaneously.

There is a new, very comprehensive tutorial on multitasking in the SPLat Knowledge Base

New timing instructions

The 8 standard timers in SPLat, plus the Pause (and related) instructions and various clever tricks have served well over the years. However one objective has been difficult to achieve: Counting up a long succession of short-ish timer intervals to make up longer intervals, for example accumulating seconds to make minutes, hours and days, without getting cumulative errors. The errors come about because there is an inevitable delay between a timer expiring and the program detecting it and restarting the timer.

Two new instructions make it much easier to get non-cumulative errors. They are called GetTick10 and GetTick100. These work on a 10mS and 100mS cycle respectively. Each provides an 8-bit counter that counts up every time interval, and can be read out. Each time it is read out it is counted down by one. The counter cannot count up past 255 or down below 0. The result is your program can have a backlog of up to 2.55s or 25.5s worth of ticks, and still not incur any lasting errors. The resultant timing accuracy is directly related to the board's timing crystal, which typically is 0.01% or better.

The more accurate timing is also significant if you are doing PID control, because it will help you make your sampling interval more accurate.

Enhanced I/O

In dialect 12 we have enhanced the byte-wide I/O instructions InputFM and OutputM. From dialect 12 on these no longer have to be on a byte boundary, i.e. 0, 8, 16 etc. You can for example now do an InputFM 12 and it will read inputs 12 through 19.

The CPU device

We have also introduced a new onboard peripheral, namely the CPU (the SPLat processor itself). What we have done is make certain attributes of the controller itself accessible to your SPLat program. Initially we have exposed:

  • Board type
  • Firmware revision
  • Dialect
  • Subroutine nesting (stacking) depth
  • Chip-specific reset flags
  • Runtime error information

These will allow a program to sense what environment it is running in, and modify its behaviour accordingly. This might allow you for example to use the same program in entry level and up-market versions of your product, using different SPLat boards. We expect to add to the list of CPU attributes over time. These are not directly dialect related things, so we could well add to the list without declaring a new dialect. The documentation is in the SPLat Knowledge Base.

Upgrade existing boards free

All SL99s and any MMi99s with Firmware V3.3 or later can be reFlashed to the new dialect. Please contact us regarding upgrading custom controllers.


Technical: Resolution and accuracy are not the same thing!

A common mistake people make when specifying the analog functions of a custom SPLat controller is to confuse Accuracy and Resolution.

Accuracy.

An accuracy specification says that if the reading of a measuring device is "so and so", then the real amount being measured is within x (accuracy spec) of that figure. For example, I may have an old fashioned glass and mercury thermometer that is specified at ±1°C accuracy. If it reads 20°C in my office then I know the actual temperature is somewhere between 19°C and 21°C.

Resolution.

Resolution is the ability of a measuring system to detect small changes. The smaller the detectable change, the higher the resolution. Take my old fashioned thermometer. It has graduation marks for each degree, plus 5 subdivisions (it's actually a very nice laboratory thermometer). That means its resolution is 0.2°C.

Accuracy is expensive, resolution is cheap

If I am making glass thermometers, it will make very little difference to the cost if I engrave 2 ticks or 5 ticks per degree on the scale. However, if I want it to be accurate to 0.1°C rather than 1°C it is going to cost me a lot more. For 0.1°C I may have to spend thousands of dollars on a reference bath or some-such, and spend a lot of time on each and every thermometer I make, checking and calibrating it.

The same applies in the electronic world of SPLat. Current standard SPLat controllers like the SL99 have analog inputs specified at 8 bits (1 part in 256) resolution and ±1.5% accuracy (1 part in 67). That means that it can resolve a change of about 40mV, but its result may be accurate to only 150mV. Is this in any way dishonest of us? Not at all. Resolution without accuracy can be very useful.

Can we make the analog functions on SPLats more accurate, with higher resolution? Yes we can, and we do, on custom SPLat boards. Our SCEL:RTD16 circuit has an analog to digital converter with 18-bit resolution. It is used for very accurate temperature measurement in autoclaves. Each end product must be individually calibrated against a traceable standard, simply because the high grade RTD probes used are still only accurate to 0.5°C.


Name change

Our proprietary communications protocol has been renamed from LiveData to SPLatLink. This better reflects the expanding capability and broadening range of applications it is finding.

The original name was coined because we originally devised a method of peeking and poking at the data of a SPLat controller while running a program, as a debugging aid. As we enhanced the protocol, and introduced the SLCom ActiveX implementation, more and more users started using it in final systems, such as SCADA applications.

The ActiveX control will retain its name, SLCom, for compatibility reasons. However, a new ActiveX with a new name and greatly enhanced capabilities will be released probably 2Q04.


SPLat Happenings Newsletter May 2003

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions at the end.

Once more it's been far too long since the previous newsletter. In fact, it's been a very busy 5 months, with a large component of product development (some being full custom product that cannot be publicised).

I was in Florida in late January, delivering and commissioning one such product. In fact, I was staying in Orlando when the Columbia disaster took place. It had a very minor personal angle: I was booked to run a seminar for NASA engineers on the Tuesday when the Columbia crashed on the preceding Saturday. I can now only hope that the US government will not be dissuaded from the need to keep pressing forward with space exploration, and preferably far beyond the cozy, routine Low Earth Orbit stuff. Just as I refuse to believe we are all alone in the universe, I equally refuse to believe that it is Mankind's Manifest Destiny to remain confined to one little planet.

In this issue

NEW: Relay expansion board, XIRO16

Programming: SLComm tested with LabVIEW

SPLanta competition winner

Shadow memory, a buried treasure

History of the PLC


NEW: Relay expansion board, XIRO16

XIRO16 is a new expansion board for SL99, MMi99 and all future XPice enabled SPLat controllers. It adds 8 relay outputs and 8 digital inputs. An SL99 or MMi99 can control up to 4 of the boards (many more with a bit more effort in programming). The XIRO16 appears to the programmer as additional simple I/O points.

The output relays are rated up to 10A at 24V. We do not recommend using them on 120/240V. The inputs are standard low voltage direct coupled logic inputs, similar to those on the SL99 and MMi99.

Xiro16: Full board

The XIRO16 is US$109 1-off (DK version), dropping to US$60 in 1K quantities (OEM version). Full product information is in the SPLat Knowledge Base.


Programming: SLComm tested with LabVIEW

Just a reminder about the existence of our SLCom.ocx communications ActiveX control. It will let you build systems where SPLat and a PC can interact. SLCom was originally developed for use in VB6 projects, but has been successfully used with National Instruments LabVIEW.

SLCom provides a simple way of implementing our SPLatLink communications protocol in a PC program. SPLatLink lets the PC access RAM (data) memory, I/O and other resources within the SPLat controller very easily, taking care of all the communications details.

Post script: SPLat now supports ModBus, and National Instruments have a free ModBus library. This would be a preferable solution.


SPLanta competition winner

You may recall our "count the SPLantas competition", which we ran for Christmas last year. We had quite a number of entries, many of which were correct, even.

The first correct entry drawn was from Iain Palmer, who is a Maintenance Officer in Wallumbilla in sunny Queensland. Iain won an SL99DK controller valued at US$129.

Congratulations to Iain and thank you to everyone to took part in the fun.


Shadow memory, a buried treasure

I recently had cause to write a fairly substantial SPLat program for a customer who had ordered a custom SPLat design. (We usually try and persuade our customers to write their own programs, but this was a special case with a custom controller and a tight deadline, so I let my arm be twisted.)

The program (and controller) is for high speed shutter door operation, and requires a number of operating parameters to be set up by installation technicians using the built in LCD display. There are two key features required for this kind of menu driven setup. The setup data must be stored in a non-volatile memory, and (ideally) it should be possible to cancel out of the setup without having actually changed the value (in case the user changes their mind).

The newly introduced shadow memory achieves this very simply. Shadow memory is a non-volatile memory that "shadows" all 128 bytes (at time of writing) of user RAM. It can be written and read in any block size from 1 byte up to the full 128 bytes. To store a particular block of RAM in non-volatile memory you just use its RAM address and block length in a ShadowWrite instruction. To restore it you use the same parameters in a ShadowRead instruction.

In the setup menus I wrote for the door control I simply let the user go through the moves, changing the setup value and altering it in RAM. If the user completes the process with the "OK" button to accept the changes, I do a ShadowWrite. If the user ends up with the Cancel button, I instead do a ShadowRead to restore the original value from shadow memory. When the program first starts up, its initialization code ShadowReads all the setup parameters.

This strategy will work only if setup mode is quite distinct from operating mode. If setup changes must be done while the program is also operating, there is a risk that a weird setup value could be used in operation. That was the case with a milk storage and chilling system I did a year or so ago. System configuration, say cycle times, can be changed while the system is operating. In that case I had to use temporary memory locations to hold the parameter being changed, and only write it to the "real" variable when the user accepted the changes. What this shows, of course, is that you have to understand the application and how the controller is going to be used, then make sure you devise or select a strategy that will meet the needs. There is no universal solution!


History of the PLC

I came across something interesting while checking out our Google ratings (#1 spot under "programmable logic controllers" plus listings on several subsequent pages!). The thing I discovered is the history of the PLC. You can find it at http://www.barn.org/FILES/historyofplc.html

This is a verbal history of the very first PLC, narrated by it's chief architect, Dick Morley. It's well worth a look, and while you are there look around the other stuff on the site. What struck me about it was the pre-occupation with making a box that was reliable. I remember those days (1968-ish) quite well myself. I was an even younger :-) engineer then, and involved in a couple of ways with selling minicomputers, mainly the PDP8 and a bit later the original Nova. The machines cost something like A$25,000 (that was about 5 year's pay to me at the time), and came with a 3 month warranty. That's right, 3 months! The rationale was that the semiconductor components exhibited "infant mortality", so if it was going to fail it would be within the first 3 months. After that you were on your own.

So, is SPLat a PLC? By Dick Morley's standards, no it's not. SPLat is an embeddable board level controller aimed at OEMs. We see our customers as being product makers who need a controller in their product. Mr Morley's programmable controller was intended for factory automation, which is a quite different thing. Dick Morley's product came in a cast iron case. Ours comes as a bare board, ready for you to integrate into your own product packaging.

Anyhow, take a look at the site. It's a fascinating read.


SPLat Happenings Newsletter December 2002

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions at the end.

SPLanta 100 wide

Well, the festive season is just about upon us! Here in Australia we are stocking up on sunburn cream and checking out the barbecue equipment, while some of our friends "up over" are already exercising their snow shovels.

At SPLat Controls, as always happens, we are being flooded with "must have it by Christmas" jobs. I remember, many years ago as a young engineer soldering on (sic) to finish a "must have it by Christmas" project for a major mining company. Meanwhile a noisy office Christmas party was going on upstairs directly above my lab, and I was missing out on my chance to chat up that cute little secretary over a glass of punch!. Three months later I was "on site" at said mining company on another job and inquired about my brainchild. "Oh, that" was the reply, "It's still down in the warehouse, we haven't looked at it yet". That was when I decided never, never, ever again to panic whenever a customer absolutely positively had to have it by Christmas. But guess what, it still happens every year!

Here in Australia business just about comes to a standstill for 3-4 weeks over Christmas. Because it's our summer and school summer holidays, people also take their annual leave (that's Aussie for vacation), and many businesses simply shut down for 3-4 weeks. That is particularly the case with companies like printed circuit manufacturers with process lines requiring full staffing to operate. Given that we always seem to be developing something in a hurry for someone, and that it requires printed circuits, it always gets "interesting" for us at this time of year. As it happens, we are developing something that's in a hurry, for a customer in Florida (hi Bruce!), and we've just had a mad scramble to get the PCB artwork in before the 12 noon on 12/12 deadline. We made it! So in January we're off to Florida to deliver the prototypes and help with the commissioning (how's that for personalised service :-). On the same trip we'll be visiting Diamond Telemetry in Tulsa, Oklahoma, who make very competitive (try half the price of competing systems!) SPLat-based SCADA systems for pumping applications. Finally we will have some meetings with potential SPLat reps in Southern California and spending some R&R time with friends in San Diego. May even make it down south of the border for the first time!

Where-ever you are, whatever your creed, I hope the coming weeks, and the coming year, are good to you and that you spend a pleasant time with those you care for.

Season's greetings from us all at SPLat Controls.

In this issue

WIN a SPLat!

SPLat, test thyself!

Programming: New instructions

New programming documentation

WARNING: Do NOT email it to a friend!


WIN a SPLat!

SPLanta 100 wide

Being the festive season and all, I've just decorated the SPLat website with SPLantas. The question is, how many SPLantas are there on the SPLat website?

I have an SL99DK to give away to one lucky person who can correctly count the number of SPLantas (We will alternatively let the winner apply the value of an SL99DK towards purchase of another SPLat controller).

To enter you should do the following:

  1. Visit the SPLat Website and locate all the SPLantas. Do not include the two in this newsletter!
  2. Copy and paste the URL (page address) for each page with a SPLanta into an email
  3. Send the email to [email address deleted due to spam] including your name and delivery address
  4. Start hoping

Entries close January 6th 2003. A random selection will be made from the correct entries. Employees of SPLat Controls or associated sales representatives may not compete.

The winner will be announced in the next newsletter.

PS: We will pay airmail delivery but all taxes and duties in your country will be your responsibility.


SPLat, test thyself!

One perfect application for SPLat controllers is in testers for electronic products. You should not be surprised, then, to learn that we use them ourselves. In fact, many SPLat boards litterally test themselves!

In the 'photo is the tester we recently completed for a custom SPLat for AgiDrive, makers of sophisticated barrel controls for concrete trucks (the picture was taken just before the lid went on and the labels got printed!). The controller itself is to the left, and comprises a 2-board set, a main board and an operator interface (OI). The two are interconnected by a cable that can be up to 10m (30 feet) long.

To the right is the tester, which has been built into a plastic box. Closest to the camera, on the lid and in the base of the box, are a pair of XBIO16 expansion boards. The brown board is a piece of Veroboard (prototyping board) with some signal relays and sundry bits and pieces. The rest of the box contains two transformers, a heavy duty relay and a few other bits.

Now here comes the magic: It is the unit under test (UUT) that runs the test, using a SPLatWare program that is loaded into it at the same time as the SPLat operating firmware. And what "squirts" these programs into the UUT? The test box itself, with the help of a PC.

With this setup the SPLat AgiDrive controller subjects itself to a very comprehensive set of tests, designed to verify the operation of every circuit on the 2-board set. Operator input is via the push buttons on the operator interface board. Every test that can be performed automatically is done automatically. Where manual intervention is required, directions to the operator are displayed on the LCD, like "Push button 1", "confirm LED 4" etc.

The tester performs functional and parametric tests on every input and every output, including analogs, on buttons and indicators and on DIP switches (with help from the operator).

The orange objects on the brown board are signal relays. The are used to manipulate analog voltages and currents. Another relay switches the power supply voltage between 12V and 24V, so the UUT is fully tested at both supply voltages. An external voltmeter is used to initially adjust the reference voltage on the UUT, thereafter all analog test readings are done by the UUT itself.

The AgiDrive tester is particularly elaborate, because the AgiDrive controller contains several quite esoteric interfaces, including a Danfoss proportional drive output and a 0-125mA high side constant current source. These are things you simply cannot buy as commercial modules. Going for a custom SPLat allowed the AgiDrive people to consolidate all their electronics into a single board (plus the operator interface). The alternatives for them would have been an industrial PLC plus a collection of custom built interface circuits, or a fully custom controller board. The former would always be clumsy and unwieldy, while the latter would have cost 10-20 times more to develop.

Our standard products also test themselves. Since it acquired an LCD, the MMi99 became self-testing. The SL99 uses a very similar setup to the MMi99, though without an LCD we had to make very creative use of flashing LEDs!


Programming: New instructions

We recently introduced 4 new instructions. These are

KBeepOn, KBeepOff.

These two allow you to switch off (and back on) the automatic beep the MMi99 generates when a front panel push button (key) is read out with an InputK instruction (or one of its close relatives). They were done on request from a customer ... just shows we do listen!

DecMRetIfZ, DecMRetIfNZ

These allow conditional return from a subroutine based on counting down a memory counter.

The new instructions are available in dialect 11. If you are using boards with reFlash capability, you can upgrade them to dialect 11. In this instance that would apply most specifically if you are using MMi99 and would like to turn off the automatic key beep.


New programming documentation

At last! We finally managed to get the descriptions of all SPLat instructions (all 200+) together in one place. Untill now the older instructions have been only in the Classic help file that's part of SPLat/PC, while the newer ones (basically, anything introduced since the start of 2002) were in the SPLat Knowledge Base (SKB).

Now everything is in the SKB. They are under "Programming Reference", and organized both alphabetically and by functional groups. Now it should be much easier for you to find what you want or simply to look up the correct syntax of an instruction.

The other thing we've done is to generate a Microsoft Word document of the alphabetical instruction reference. That will let you print it out if you wish.


WARNING: Do NOT email it to a friend!

Another Internet Cautionary Tale: Beware of "Email it to a friend" buttons.

My partner Keith never used to get Spam. Then a friend of his did an "email to a friend" to him from a website containing lots of funny Flash cartoons.

Now Keith is receiving spam, by the bucket load! The "email it to a friend" was simply a trick to collect a poor innocent victim's email address for spamming.

Please, always be wary of anything you do on the Internet than can expose your email address, or that of a friend, to the spammers.


SPLat Happenings Newsletter October 2002

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions at the end.

The dominant event for us this month was exhibiting in the Austronics 2002 exhibition. As is often the case in Australia, this was run concurrently with (and co-located with) Automate 2002 and Electrix 2002. I've included a couple of photos from the show, which you will be able to view only in the online version of the newsletter.

In this issue

Show report

BEWARE: Virus hoaxes

TECHNICAL: Excellent stepper motor tutorial

Tutorial: Temperature measurement

SPice 10200 documentation

NEW FEATURE: Fast counter goes to 18kHz


Show report

The show went for 3 days, October 15th to 17th. And what a busy 3 days they were! I'm not going to suggest ours was the busiest stand in the show, but we certainly didn't have many idle spells.

Our first show was the same event back in 1998 (and was the main story in our very first newsletter). At that time we were relatively new kids on the block as far as offering an off the shelf programmable controller was concerned (we could even then boast of 18 years of making custom OEM controls). This time we had a real track record to boast about, with SPLats in Carrier airconditioners, pallet wrappers and even getting ready for space travel. We had standard SPLats and custom SPLats on display, and formally announced the new SL99.

We kept things interesting with a lunar lander game, baloons (with an associated prize), a demonstration of SCADA using SPLat over radio and a model boomgate to demonstrate how easy FastTrack programming is.

Previous Topic

Next Topic

In This Chapter

What we displayed

Lunar lander demonstration

Prize winners


What we displayed

We displayed a variety of SPLat controllers at the exhibition, some regular products and some custom designs, plus one very special adaption on a standard product. We also showed off SPLat's ability to communicate with a PC via 2-way data radios.

In This Section

CA20 HVAC controller

AgiDrive 01 concrete barrel controller

Standard products

Space-bound SL99

SPLat on the radio


CA20 HVAC controller

The CA20 was designed for Carrier-Apac, and is used in their industrial/commercial heat pumps. Special features of this board include:

  • Operates off 24VAC
  • Relays for driving outdoor fans with 20A running/ 80A surge capacity.
  • Relays for driving external contactors and valves, life tested by us to over 2 million operations.
  • Diagnostic fault capture and display via onboard 7-segment digit.
  • A number of other custom features that embody the customer's proprietary techniques.

A special feature of this project is a PC program we wrote for the customer, using SPLatLink. This gives a display of the internal operation of the controller in a readily understood format. This allows Carrier-Apac production staff to perform test their product with no intimate knowledge of the workings of the control system.


AgiDrive 01 concrete barrel controller

The concrete barrel controller is a custom SPLat used in the AgiDrive control system. The AgiDrive system is perhaps the most advanced barrel control system in the world, offering barrel loads of features to improve quality, maximise productivity and reduce maintenance.

The SPLat contains many features aimed specifically at the customer's needs. These include a dash-mountable operator control panel that connects to the main board via up to 10m of standard CAT5 cable (for ease of supply and installation), and a main board with special purpose interfaces for proportional hydraulic valves, engine control modules etc. The operator control panel contains an LCD, two LED bargraphs, annunciator LEDs, beeper and push buttons. Programming is as easy as a standard SPLat. For example, the operator control panel appears like a regular OnBoard LCD (OBLCD) just like on an MMi99, and the bargraphs appear as analog outputs. The latter idea means the programmer doesn't need to worry about figuring which LEDs to turn on, and if a future technology should allow dramatically more bargraph segments there would be no need to change the SPLatWare.


Standard products

Our standard MMi99 was on display, as well as the SL99 (which was formally announced at the show). We also had the SPice 10200 add-on and the relatively new XBIO16 expansion board on show.


Space-bound SL99

sl99 prepared for nasa testing

The picture above is of a very special SPLat. This one is destined for orbit.

A SPLat customer in Maryland is designing a scientific experiment that (it is hoped) will be flown on a Space Shuttle within the next several months. They have selected SPLat to control the experiment.

The controller is an SL99. It has been specially prepared to meet NASA's exacting standards with several measures:

  1. All connectors have been removed to reduce height and eliminate plug-in connections, and replaced with soldered in wires where required.
  2. The regulator chip has been laid down, and the associated potentiometer has been eliminated. Because the application doesn't require analog capability, the loss of trimming is unimportant.
  3. Electrolytic capacitors have been replaced with tantalums, so they can survive vacuum testing.
  4. The whole thing has received 3 dip coats of silicone conformal coating to improve vibration tolerance.

At time of writing the boards are at the Marshal Space Flight Center have been subject to and passed 2 of the 3 test criteria. These are EMC (Electro Magnetic Compatibility) and vibration. The third test is outgassing, essentially to ensure that equipment doesn't generate fumes that may upset astronauts or sensitive equipment.

STOP PRESS: The third and final test has been sucessfully completed. We are now simply waiting for a place on a launch!


SPLat on the radio

Our lunar lander game featured our SPLatLink communications protocol being used in a radio data link back to a PC. In the PC we had two programs, one written in National Instruments labVIEW and one written in Visual Basic 6.

Both program have the ability to monitor and display the status of the game, and it is also possible to actually "fly" the lander and successfully land it via the radio link.

The data radios we used are model TC900D from Trio DataCom.


Lunar lander demonstration

Our lunar lander game at the show, while a lot of fun, was designed with a serious intent. It shows off very well the many abilities that SPLat has evolved over the years, and its capacity to perform complex control tasks involving sequencing, timing, measurement and floating point calculations.

The basis of the game is one of the very earliest computer games, reputed to have first been written for the PDP8 (the best selling minicomputer in the early 70's, which I cut my teeth on). In our version you find yourself free-falling towards the moon, starting at 2000m, and must use your rocket to make a gentle landing. The only control you have is the rocket "Burn" button.

The game is made with an MMi99 board. There is a fuel tank, consisting of a 1m high clear acrylic tube, pumps to fill and drain the tank and solenoid valves.

The fuel level is monitored in 2 ways. Firstly there are conductivity electrodes at the top (full) and bottom (empty) ends of the tank. Then there is a Honeywell pressure sensor at the bottom. The conductivity probes interface to the MMi99 via a circuit identical to the 2-channel fluid level switch on our SPice 10200 add-on board. The pressure sensor interfaces via an amplifier that has a programmable offset and fixed gain. Both lots of interface circuitry are on a special SPice board we made for this application.

Apart from the special SPice board and the board holding the driver resistors for the LED clusters, this demonstration was built totally out of off the shelf SPLat parts and DIN rail mounted relays. The special SPice board exemplifies what can be economically done for our customers in quantities as low as 20 units.

At the bottom of the fuel tank is an assembly consisting of 16 each Red, Blue, Green and Yellow very bright LEDs. Each colour cluster is driven off a network of resistors that forms a simple 4-bit digital to analog converter. This allows the intensity of each LED colour to be set to any of 16 levels (including off). This whole lot is driven off a single XBIO16 expansion board. The end result is a fairly impressive programmable colour light show. In the photo above you see it in its blue mood.

The fuel consist of plain old H2O with a tiny amount of milk added, giving it some turbidity to catch the light.

Finally there is a cabin temperature sensor, comprising a thermistor probe mounted just next to the MMi99 front panel.

The MMi99 is fitted with an LCD. We also built a large 2 line by 16 character LED display which exactly mimics the LCD, and is readable up to 20 or 30m away.

The SPLat program provides a menu structure with a number of choices. These include "Land on moon", "Refuel Lander" and "Maintenance". Under maintenance you have manual pump control with fill and drain via two buttons, and "Calibrate".

The calibrate function automatically calibrates the pressure sensor to zero and 100%. It does this using the conductivity switches. First it drains the tank (pumps out all the water ... sorry, fuel!) until it "finds" the bottom conductivity electrode. At that point it programmatically adjusts the offset to the pressure sensor for 5 counts (this is an "offset zero", used because it's risky to try and find true zero when negative numbers are impossible). Then it fills the tank until it finds the top electrode, takes a pressure reading and calculates the scale factor to convert from raw pressure reading to percentage of fuel in the tank.

The fun part is the actual lander game. Providing the tank is full, you can start the landing process. The trick is to somehow land at less than 5m/s with at least 50% of your fuel intact. It's not easy, but it can be done.

During game play the SPLat program is doing a lot of things:

  • Calculating height and velocity ten times per second, taking into account gravity and rocket thrust (if burning).
  • Calculating remaining fuel.
  • Evaluating the status to determine if landing (height <= 0) has taken place.
  • Controlling the drain pump so the height of fuel in the tank matches the calculated value. This "cheat" ensures that the game will be reproducible. We had no hope of making the pumps pump at a precisely controlled rate.
  • Whenever the player is burning the rocket, generating a flame effect on the LEDs. This consists of a random number generator and some bit masking tweaked to get a visually pleasing effect. It looks good!
  • Measuring cabin temperature
  • Displaying everything.

Finally, the MMi99 is connected back to a PC via a data radio link. The PC displays Fuel, Height, Speed and cabin temperature in realtime. It is even possible to play the game from the PC, with the MMi99 front panel push buttons replicated on the PC screen. We had two versions of the program in the PC. One was written in Visual Basic 6, the other in National Instruments labVIEW. Thanks to Trio Datacom for the loan of two radios!


Prize winners

dvd prize presentation 2002

During the show we gave away SPLat baloons. Realising not everybody would want to carry our logo around the exhibition hall for us, we offered an incentive in the form of some attractive prizes.

First prize was a DVD player, and it was won by Tracy Saliba, a 3rd year computer science and electronics engineering student at LaTrobe university (pictured above receiving her prize from your Humble Correspondent). When we called Tracy to tell her she had won, she was already having a good day. She had just got her 3rd year design assignment working. She has made en "electronic poodle". I asked her for a brief write-up and she obliged, and even brought her device down to our office so we could photograph it. Tracys description and the photo are reproduced below.

The "daily" prizes were double passes to Village Gold Class cinemas. They were won by:

David Tripp, an electrical contractor.

James Mullins, an engineering student at Deakin university who has just completed his Bachelor's and is thinking of going on to do his Ph.D.

Carlo Sergi, an engineering student at LaTrobe, and a class mate of Tracy's

And in case you think it was rigged, with 3 out of 4 winners being students, you're wrong. I put it down to these being intelligent young people who recognized a great product and were happy to carry our baloons!!

In This Section

Tracy's electronic poodle


Tracy's electronic poodle

tracys electronic poodle

When Tracy came around to our office to pick up her prize, she brought and demonstrated her "Electronic Poodle". Here is her own description of her project (nothing to do with SPLats but interesting) ...

La Trobe University

Third Year Engineering Design Project

Electronic Poodle

Hi, my name is Tracy Saliba and I am currently completing a double degree of computer science/electronic engineering at La Trobe University. I have just finished my third year project which was to build a voice activated motor design called the Electronic Poodle.

What is the Electronic Poodle?

The electronic poodle is a system that takes input from the user via a microphone. The electronic poodle has been trained to recognize two voice commands. The commands are "STOP" and "GO". When the poodle hears the command "GO" it will activate a stepper motor causing it to rotate. When the command "STOP" is heard, the stepper motor will be turned off.

How does it work?

When a voice command is spoken into the microphone, the signal is amplified to produce a signal with a maximum of 5 volts. This amplified signal is then sent through a precision rectifier. This is where the negative part of the signal is discarded. An envelope detector circuit is then used to finally prepare the signal before it is sent through to an analog to digital converter. The ADC presents a digital representation of the signal and it is sent into the microchip PIC. With the aid of a PIC program written in assembly language, the digital signal is analysed and it will determine if the signal received was a "STOP" or a "GO". The stepper motor is then turned on or off via the stepper motor controller chip.

The design also incorporates four indication LED's so the user is aware of what is going on. One LED indicates when a signal is being sent, (i.e. the user is speaking loud enough into the microphone), another LED will indicate whether the sound was a valid length, another LED will indicate if the sound is being tested for a "STOP" signal, and the final LED will indicate whether the signal is being tested for a "GO" signal.


BEWARE: Virus hoaxes

About the easiest way anyone can cause malicious computer damage is to start an email virus hoax. You don't need any programming skills, just the ability to write an email. The worst type of hoax is one that tells you so-and-so file on your computer is a virus, and advises you to delete it at once. If you fall for such a hoax you could do irreparable damage to your system software, and by passing on the hoax cause your friends to damage their computers as well.

So, next time you receive an email warning about a 'New Virus', check it out before you delete any files or forward the email to all your friends.

Checking out a hoax is easy. Just copy the name of the offending file or the name of the virus into your favourite search engine (I use www.google.com), add the word 'hoax' and see what you turn up. You can also try looking up the alleged virus name in one of the anti-virus software sites. Most makers of anti-virus software provide lists of genuine and hoax viruses.


TECHNICAL: Excellent stepper motor tutorial

I came across an excellent tutorial on stepper motors at

http://www.cs.uiowa.edu/~jones/step/#abstract

This goes into the theory of how steppers work, some math for those who can be bothered, and contains some excellent material on controlling and driving steppers. If you need to get up to speed on steppers, this is well worth a look.


Tutorial: Temperature measurement

We have recently completed a tutorial on temperature measurement using thermistors. As you may be aware, we offer thermistors temperature measurement as a standard feature on several of our controllers.

The tutorial covers some of the theory, then goes into the specifics of using thermistors with SPLat. A companion spreadsheet is included, which greatly simplifies the calculations required, plus a "boiler plate" program makes programming easy. Now you can incorporate temperature measurements into your controls at lower cost than ever before.

The tutorial is part of the SPLat Knowledge Base. Click here to access it.


SPice 10200 documentation

The SPice 10200 is an add-on board for MMi99 and SL99 that adds 2 channels of water level switching (conductivity switches) and 3 channels of temperature measurement. We've had this board out for a while, as it was initially designed for a specific customer's need, but we've only just released the documentation publicly.

The SPice concept allows custom features to be added to standard SPLat boards quickly and inexpensively. It's the kind of thing that is helping SPLat give OEM customers, large and small, an unfair advantage. The huge advantage of SPice boards is that they are relatively simple to design and make, and simple to program. That means we can economically engineer even a small batch (as few as 20) of SPice boards for your needs.


NEW FEATURE: Fast counter goes to 18kHz

We have just completed work on a new fast counter feature in SPLat controllers. It is available in reFlashable MMi99s, SL99s and a recently completed custom controller. We call it "OnBoard Counter type A" (in case we in future come up with types B, C etc), or OBCA for short.

The OBCA gives you frequency counting at up to 18KHz, making it suitable for diesel engine speed measurement, belt speed (using a pulse encoder) and other pulse rate measurement applications.

The OBCA is fully documented in the SPLat Knowledge Base. Firmware upgrades with OBCA are available on our website, as is an update SPLat/PC V6.10.7 which supports the OBCA_ instructions.


SPLat Happenings Newsletter September 2002

This newsletter is sent to anyone who has expressed interest in SPLat programmable controller products. Subscribe/unsubscribe instructions at the end.

Welcome to the September 2002 edition of our SPLat Controller newsletter. You will notice the format has changed. That's because I've decided to try and produce it in a format that's easily incorporated into the SPLat Knowledge Base.

It has been a full nine month since the last newsletter. Far too long! I am truly sorry I haven't managed to get one out before. My only excuse is that they have been very full months. Not only have I been all around Australia visiting SPLat users and negotiating custom SPLat designs, I have also been in the US (Los Angeles). On top of that we have been doing significant work on new products, including some very significant stuff I can't announce just yet.

In this issue

NEW WEBSITE

Upcoming exhibition

NEW: SL99 Controller

NEW: XBIO16 expansion board

TECHNICAL: Excellent relay information

NEW FEATURE: Shadow memory

NEW CAPABILITY: Upgrade your own firmware

WWW: Become a Power Searcher

LAST CHANCE: SP108 is being discontinued


NEW WEBSITE

If you have visited our website in the last few months, you will have noticed it has been totally redesigned.

If you haven't seen the new website yet, please take a look and let me know what you think. http://splatco.com

The end result is a new, consistently applied look and feel, a much better navigation menu, and very much more content.

Perhaps the most significant result of the redesign is that the SPLat Knowledge Base (SKB) has been tightly integrated into the main website. The SKB is the central repository where all product documentation, technical articles and other support materials will be stored. The SKB was started early late 2001, and all future documentation will go there. Because any documentation change can be uploaded to the website instantly, it means you can be sure that whatever is there is absolutely up to date. FYI, the SKB is generated using Author-IT, a program that allows you to write material once then generate it as MS Word documents, web pages or help files. This newsletter is written in Author-IT as part of the SKB. When it's done I can upload it to the website at the same time as I generate a Word document, save that as plain text and send it out as a plain text email.

We have also created a File Resources area. This is a somewhat random collection of SPLat related files such as outline drawings, programming examples etc. I am deliberately not making an HTML index page, because the maintenance would become a nightmare. Instead, I invite you to simply go there and browse through the directories and see if there is something useful there. If there is something you could use but can't find, email me and we will consider creating it. The File Resources area is accessible via FTP (don't worry, your regular browser can do it).

The other Big Thing the redesign was designed to accommodate is reFlash technology. That is described elsewhere in this newsletter.

We have also (finally) bowed to the inevitable and incorporated some Java Script and session cookies. This allowed us to create a simple shopping cart style online ordering system, with automatic calculation of prices and shipping costs. You can also select between various way of placing your order (online, fax or email), and the system will generate your order form in a suitable format.

What this all means for you is that the SPLat website will become a one stop shop for all SPLat information all the way from sales to nitty gritty technical stuff, and it will all hang together in a unified structure.


Upcoming exhibition

SPLat Controls will be exhibiting at the Austronics exhibition at the Melbourne Exhibition Centre (fondly know as Jeff's Shed after a well know radio host :-). The show is October 15-17 2002.

If you are in Melbourne, please come and see us there. Not only will we have our products on show, we are also working on a demonstration setup that will be both entertaining and (we think) a pretty impressive demonstration of what SPLat can do.


NEW: SL99 Controller

The new SL99 controller is currently in production and will be available for sale in a matter of days. The SL99 replaces the SL88 (much like MMi99 replaced MMi88) and offers you a number of new features.

The SL99 gives you:

  • 8 digital inputs, expandable to 40+
  • 8 digital outputs, expandable to 40+
  • 1 analog input, can be extended to 5 with SPice add-on
  • 1 analog output, can be extended to 3 with SPice add-on
  • 2 program definable option links
  • Twice as much program memory (9000+ bytes)
  • Twice as much RAM (128 bytes)
  • Ability to work through a modem.
  • 7x more speed than SL88
  • Same size and mounting as SL88
  • Support the same SPice boards as MMi99 for special purpose I/O
  • All plug-in connections for faster, easier production line installation

Pricing is the same as the superceded SL88.

The SL99 is suitable for applications requiring modest amounts of I/O without a built in operator interface. It is virtually an MMi99 without the operator interface.

The SL99 information will be available on our website in a few days.


NEW: XBIO16 expansion board

The XBIO16 expansion board for MMi99, SL99 ( and future controllers) has been released. This has turned out to be a very nice board, very simple to use and program.

The XBIO16 provides 16 bidirectional digital I/O points. 'Bidirectional' means each one can be used as an input or as an output (though not as both at once, except under very special circumstances). That gives you a great deal of flexibility. IF you are familiar with the OEM32/36 you will know about bidirectional I/O.

As outputs each point can sink (ground switch) up to 400mA (identical spec to the MMi99 outputs). As an input it has the standard NPN/dry contact interface, though with a slightly enhance voltage threshold for improved noise immunity.


TECHNICAL: Excellent relay information

I recently discovered and excellent online source of technical information of the proper care and feeding of relays (and similar matters). The website is http://www.pandbrelays.com/application.asp.

If you have the slightest interest in getting the most out of the relays you use, you should spend a few minutes checking out these files. Here are a couple of sample titles:

  • Coil Suppression Can Reduce Relay Life
  • Operating DC Relays from AC and Vice-Versa


NEW FEATURE: Shadow memory

A new feature of the MMi99 (and all subsequently released products) is 'Shadow Memory'.

Shadow memory is an extension of the non-volatile 'Permanent Memory' that has existed in SPLat for a very long time.

With the shadow memory feature every single byte of RAM (user memory) is now potentially non-volatile. Behind every byte of RAM there now lurks a byte of non-volatile Flash memory. All you have to do to use it is to shadow-write it when required, and shadow-read it at the start of your program. That means you can selectively ensure that any part of RAM will retain its data when power goes off.

There are some new instructions associated with the shadow memory feature. These are ShadowClear, ShadowRead and ShadowWrite. Each one lets you specify a starting address and byte count. The disadvantage compared to the older PermStore and PermRecall instructions is lower endurance, meaning the flash memory will eventually wear out.


NEW CAPABILITY: Upgrade your own firmware

We have recently introduced a new level of customer support with our reFlash capability. The MMi99, SL99 and all future 'second generation' SPLat controllers, can be field-upgraded with new Firmware. What that means to you is that if and when we introduce new programming features such as new instructions or virtual SPx peripherals, you will be able to upgrade your existing controller boards. This is especially useful for educational users who have classroom sets, or for customers who have a board dedicated to inhouse development work.

reFlashing requires a special utility program to be run in your PC. It is available from our website. In future it will also be on the resources CD supplied with Developer's Kit versions of all our controller products. reFlashing also requires the actual firmware file to be 'burned' into the controller board. More information.


WWW: Become a Power Searcher

The fact you are reading this means you are using the Internet. In fact, not too many SPLat users are not online.

The web is an invaluable resource for anyone in a technical occupation, not least as a source of information of all kinds. Most of us use search engines for hunting down information.

Here's a trick that will increase the speed with which you can locate the information you need:

  1. In your favourite search engine, type in the search term.
  2. When the results come back, scan through the summaries to pick out the best looking ones. When you see a good prospect RIGHT CLICK on it, and from the context menu select 'Open in new window'.
  3. Don't wait for the selected item to download (not even if you have broadband). Instead, use ALT+TAB to get back to the search results page, and continue your scan of the results, right clicking each one in turn and opening it in a new window.

What will happen is that you will wind up with many simultaneous browser windows, each downloading a new search result. You can easily get 20 pages to view in 30-40 seconds, and start reading the early arrivals while you wait for the slower ones.

I figured this technique out a few months ago, and I am now getting results maybe 5 times faster than before. Give it a try ... you'll never go back to waiting for one page at a time to download.


LAST CHANCE: SP108 is being discontinued

The original SPLat controller, the SP108, is about to be discontinued.

After nearly 7 years of continuous production, we have finally decided to discontinue the SP108. It started life as the SP10-8, got changed to SP10-8A and was finally dubbed SP108 (with several incremental improvements along the way). The reason we are discontinuing the product, and not replacing it with a 2nd generation equivalent, is simply that sales have declined. Clearly you, our users, are looking for something different.

I guess it also shows how our efforts to respond to customer feedback have worked well. The product we have today is quite a bit different from what we had 7 years ago, and the difference is that they reflect all the feedback we have had from users.

Anyhow, we have decided there will be one more batch of SP108s made. If you need these controller, please therefore get your orders in quickly. The cutoff date is September 30. After that date a minimum order quantity will apply.


Warranties, Legal and Policies

Previous Topic

Next Topic

In This Section

Standard terms and conditions of sale

Standard product guarantee

3rd party products

Product disclaimer

Returns policy

Order cancellations

Re-stocking fees

Post-Nuke repair policy

SPLat educational discount policy

Copyright

Trademarks


Standard terms and conditions of sale

1. Interpretation

In these conditions:

(a) `Seller' means SPLat Controls Pty. Ltd. ABN 21 052 484 689 of 2/12 Peninsula Blvd. Seaford, Victoria, 3198, which is the seller of the goods.

(b) `Buyer' means the buyer named above or the client or customer of the seller in any agreement to which these terms of business are annexed.

(c) `Goods' means the products and services specified in any agreements between the buyer and the seller from time to time.

(d) Nothing in these conditions shall be read or applied so as to exclude, restrict or modify or have the effect of excluding, restricting or modifying any condition, warranty, guarantee, right or remedy implied by law (including the Trade Practices Act 1974) and which by law cannot be excluded, restricted or modified.

2. Terms of sale

The goods and all other products and services sold by the seller are sold on these terms and conditions.

3. Seller's quotations

Unless previously withdrawn, seller's quotations are open for acceptance within the period stated in them or, when no period is so stated, within 60 days only after its date. The seller reserves the right to refuse any order based on this quotation within 7 days after the receipt of the order.

4. Packing

The cost of any special packing and packing materials used in relation to the goods are at the buyer's expense notwithstanding that such cost may have been omitted from any quotation.

5. Shortage

The buyer waives any claim for shortage of any goods delivered if a claim in respect for short delivery has not been lodged with seller within seven (7) days from the date of receipt of goods by the buyer.

6. Drawings, etc.

(a) Descriptions, illustrations and performances contained in catalogues, price lists and other advertising matter do not form part of the contract of sale of the goods or of the description applied to the goods.

(b) Where specifications, drawings or other particulars are supplied by the buyer, the seller's price is made on estimates of quantities required. If there are any adjustments in quantities above or below the quantities estimated by seller and set out in a quotation, then any such increase or decrease are to be adjusted on a unit rate basis according to unit prices set out in the quotation.

7. Performance

Any performance figures given by the seller are estimates only. The seller is under no liability for damages for failure of the goods to attain such figures.

8. Acknowledgment regarding facilities for repairs or parts

The buyer acknowledges that the seller does not promise or represent that facilities for the repair of the goods, or that parts of the goods are or will be available. The buyer must ensure that each purchaser of the goods from the buyer receives notice that the seller does not promise that facilities for the repair of the goods will be available; or parts for the goods will be available.

9. Delivery

(a) The delivery times made known to the buyer are estimates only and the seller is not be liable for late delivery or non-delivery.

(b) The seller is not be liable for any loss, damage or delay occasioned to the buyer or its customers arising from late or non-delivery or late installation of the goods.

(c) The seller may at its option deliver the goods to the buyer in any number of instalments unless there is an agreement in writing between the parties to the effect that the buyer will not take delivery by instalments.

(d) If the seller delivers any of the goods by instalments, and any one of those instalments is defective for any reason:

(i) it is not a repudiation of the contract of sale formed by these conditions; and

(ii) the defective instalment is a severable breach that gives rise only to a claim for compensation in accordance with these terms.

10. Passing of risk

Risk in the goods passes to the buyer upon the earlier of:

(a) actual or constructive delivery of the goods to the buyer; or

(b) collection of the goods from the seller or any bailee or agent of the seller by the buyer's agent, carrier or courier.

11. Loss or damage in transit

(a) The seller is not responsible to the buyer or any person claiming through the buyer for any loss or damage to goods in transit caused by any event of any kind by any person (whether or not the seller is legally responsible for the person who caused or contributed to that loss or damage).

(b) The seller must provide the buyer with such assistance as may be necessary to press claims on carriers so long as the buyer:

(i) has notified the seller and the carriers in writing immediately after loss or damage is discovered on receipt of goods; and

(ii) lodges a claim for compensation on the carrier within three (3) days of the date of receipt of the goods.

12. Guarantee

12.1 The seller's liability for goods sold hereunder is limited to making good any defects by repairing the defects or at the seller's option by replacement, within a period of twelve (12) calendar months after the goods have been dispatched so long as:

(a) defects have arisen solely from faulty materials or workmanship;

(b) the damage does not arise from:

(i) improper adjustment, calibration or operation by the buyer;

(ii) the use of accessories including consumables, hardware, or software which were not manufactured by or approved in writing by the seller;

(iii) any contamination or leakages caused or induced by the buyer;

(iv) any modifications of the goods which were not authorised in writing by the seller;

(v) any misuse of the goods by the buyer or anyone for whom the buyer has legal responsibility (including a minor);

(vi) any use or operation of the goods outside of the physical, electrical or environmental specifications of the goods;

(vii) inadequate or improper maintenance of the goods.

(viii) fair wear and tear of the product in an environment in respect of which the Seller has informed the Buyer in catalogues or other product literature that the period of usefulness of the product is likely to be shorter that twelve 12 months.

(c) the goods have not received maltreatment, inattention or interference;

(d) the defective parts are promptly returned free of cost to the seller.

12.2 The seller is not liable for and the buyer releases the seller from any claims in respect of faulty or defective design of any goods supplied. In any event the seller's liability under this paragraph is limited strictly to the repair or replacement of defective parts in accordance with para of these conditions.

12.3 Except as provided in these conditions, all express and implied warranties, guarantees and conditions under statute or general law as to merchantability, description, quality, suitability or fitness of the goods for any purpose or as to design, assembly, installation, materials or workmanship or otherwise are expressly excluded. The seller is not liable for physical or financial injury, loss or damage or for consequential loss or damage of any kind arising out of the supply, layout, assembly, installation or operation of the goods or arising out of the seller's negligence or in any way whatsoever.

13. Seller's liability

13.1 The seller's liability for a breach of a condition or warranty implied by Div 2 of Pt V of the Trade Practices Act 1974 (other than s 69) is limited to:

(a) in the case of goods, any one or more of the following:

(i) the replacement of the goods or the supply of equivalent goods;

(ii) the repair of the goods;

(iii) the payment of the cost of replacing the goods or of acquiring equivalent goods;

(iv) the payment of the cost of having the goods repaired; or

(b) in the case of services:

(i) the supplying of the services again; or

(ii) the payment of the cost of having the services supplied again.

13.2 The seller's liability under s 74H of the Trade Practices Act 1975 is expressly limited to a liability to pay to the purchaser an amount equal to:

(a) the cost of replacing the goods;

(b) the cost of obtaining equivalent goods; or

(c) the cost of having the goods repaired, whichever is the lowest amount.

14. Prices

(a) Unless otherwise stated all prices quoted by vendor are net, exclusive of Goods and Services Tax (GST) and the buyer agrees to pay to the seller any GST in addition to the price.

(b) Prices quoted are those ruling at the date of issue of quotation and are based on rates of freight, insurance, customs duties, exchange, shipping expenses, sorting and stacking charges, cartage, the quotation, cost of materials, wages and other charges affecting the cost of production ruling on the date it is made.

(c) If the seller makes any alterations to the price of the goods or to any of their inputs either before acceptance of or during the currency of the contract, these alterations are for the buyer's account.

15. Payment

The purchase price in relation to goods is payable in accordance with the schedule of payments agreed between the buyer and the seller and in default of any such agreement is payable by the buyer to the seller in full immediately upon notice by the seller that the goods are ready for delivery.

16. Rights in relation to goods (Romalpa clause)

The seller reserves the following rights in relation to the goods until all accounts owed by the buyer to the seller are fully paid:

(a) ownership of the goods;

(b) to enter the buyer's premises (or the premises of any associated company or agent where the goods are located) without liability for trespass or any resulting damage and retake possession of the goods; and

(c) to keep or resell the goods including any goods repossessed pursuant to above;

If the goods are resold, or goods manufactured using the goods are sold, by the buyer, the buyer shall hold such part of the proceeds of any such sale as represents the invoice price of the goods sold or used in the manufacture of the goods sold in a separate identifiable account as the beneficial property of the seller and shall pay such amount to the seller upon request. Notwithstanding the provisions above the seller shall be entitled to maintain an action against the buyer for the purchase price and the risk of the goods shall pass to the buyer upon delivery.

17. Buyer's property

Any property of the buyer under the seller's possession, custody or control is completely at the buyer's risk as regards loss or damage caused to the property or by it.

18. Storage

The seller reserves the right to make a reasonable charge for storage if delivery instructions are not provided by the buyer within fourteen days of a request by the seller for such instructions. The parties agree that the seller may charge for storage from the first day after the seller requests the buyer to provide delivery instructions.

19. Returned goods

(a) The seller is not under any duty to accept goods returned by the buyer and will do so only on terms to be agreed in writing in each individual case.

(b) If the seller agrees to accept returned goods from the buyer under para (a) of this clause, the buyer must return the goods to the seller at the seller's place of business referred to at the head of these conditions.

20. Cancellation

No order may be cancelled except with consent in writing and on terms which will indemnify the seller against all losses.

21. Indemnity

The buyer indemnifies on a continuing basis on a fully indemnity basis the seller from and against any liability, loss, expense or demand for or arising from any false, misleading, deceptive or misdescriptive representation or statement made by the buyer in respect of the goods to any person. This indemnity survives termination of this agreement by either part for any reason.

22. Exclusion of representations and arrangements

These terms and conditions supersede and exclude all prior and other discussions, representations (contractual or otherwise) and arrangements relating to the supply of the goods or any part of the goods including, but without limiting the generality of the foregoing, those relating to the performance of the goods or any part of the goods or the results that ought to be expected from using the goods.

23. No waiver

The failure of any party to enforce the provisions of this agreement or to exercise any rights expressed in this agreement is not to be a waiver of such provisions or rights and does not affect the enforcement of this agreement.

24. Force Majeure

If by reason of any fact, circumstance, matter or thing beyond the reasonable control of the seller, the seller is unable to perform in whole or in part any obligation under this agreement the seller is relieved of that obligation under this agreement to the extent and for the period that it is so unable to perform and is not liable to the buyer in respect of such inability.

25. Buyer Acknowledgement

The Buyer acknowledges that the above provisions of these Conditions of Sale are reasonable and reflected in the price.

26. Place of contract

(a) The contract for sale of the goods is made in the state of Victoria Australia.

The parties submit all disputes arising between them to the courts of such state and any court competent to hear appeals from those courts of first instance.


Standard product guarantee

Our standard SPLat branded products are guaranteed free of defects in materials and manufacture for a period of 2 years from date of manufacture. The date of manufacture is encoded in the serial number.

Our standard terms of sale define the exact terms of our guarantee.


3rd party products

We offer certain 3rd party products, meaning products not made by us. Such products are covered by their manufacturer's original warranties. This applies even if they have been assigned a SPLat part number and/or have been sold as part of a kit.

If in doubt you should ask us for clarification before buying.


Product disclaimer

Notice to all SPLat users

SPLat controllers and accessories are not warranted as being suitable for any particular purpose. Applications examples and assistance notwithstanding, SPLat is a programmable controller component which you must yourself program and otherwise adapt to your intended purpose, and it remains entirely your responsibility to ensure that it will perform as intended in your application. The use of SPLat products in any life support application or any application where failure may result in death, injury or environmental damage is expressly prohibited. Emergency shutdown devices must always be wired independently of the control system, and must not rely on the SPLat controller for safe functioning. Our liability in the event of any failure of any SPLat product shall be limited to replacing any defective goods supplied by us.

This document and the applications information, examples (including program examples), training courses, tutorials, guides articles and white papers it contains plus all associated supporting materials such as spreadsheets, audio visual presentations and computer programs do not constitute professional advice and you should seek independent professional advice before implementing any system based upon it.

This publication and all specifications herein are subject to change without notice. You are advised to check the online version frequently and especially just prior to making a design decision, to ensure you are working with up to date information.

All product sold is subject to our terms of sale


Returns policy

All goods returned to SPLat Controls for repair or restocking must have by a Return Authorization Number (RAN). You can get an RAN by contacting SPLat Controls.

Goods returned for repair should have a note attached describing the nature of the fault and the circumstances surrounding how it happened.

An extra charge may be applied to goods returned as faulty where there is in inadequate description of the fault or if no fault is found.


Order cancellations

If a customer cancels an order prior to shipment, we may charge a cancellation fee.

For off the shelf product the cancellation fee is 10% of the value of the goods plus USD20 per order processing charge.

For custom made product or goods made to order the cancellation fee is up to 100% of the price of the goods, depending on our determination of where in the manufacturing process the goods are at the time of cancellation.


Re-stocking fees

Unwanted standard product returned to us for credit will be accepted subject to our determination, and will be levied a re-stocking/re-testing fee. The fee will be higher if the packaging seals have been broken.

For current model off the shelf product current fees are 20% if the seal is broken, 10% if unbroken and goods are undamaged. If the goods are damaged we may refuse to accept them or charge the Post Nuke Repair price. A minimum restocking fee of USD25 per item applies.

For superseded model off the shelf product current fees are 50% if the seal is broken, 25% if unbroken and goods are undamaged. If the goods are damaged we may refuse to accept them or charge the Post Nuke Repair price. A minimum restocking fee of USD50 per item applies.

Goods will not be accepted for return more than 60 days after date of purchase.

Custom products or product built to order may not be returned for credit.


Post-Nuke repair policy

It is our policy to repair, or at our option replace, any accidentally damaged SPLat controller board for a fixed price of approximately ½ the one-off price of the board, subject to the following conditions:

  1. Boards which we judge to have been subjected to attempted modifications or repairs or subject to careless or clumsy treatment are excluded, unless explicitly authorized by us in advance. This includes making connections using inappropriate means such as soldering to plug-in pins.
  2. It doesn't matter what condition the board is in. As long as it is recognizably a standard SPLat board and contains its processor chip this policy applies.
  3. We may substitute the same or a later release of the same product.
  4. If the product is a discontinued line we may substitute an equivalent current product where one exists, or one of equal or greater capacity if one exists.
  5. User's programming in the damaged board will not be preserved.
  6. The Post Nuke repair price will be that published in the most recent price list for the market (region, country etc.) where the replacement board is to be shipped. If no such price is published then the applicable price will be half the one-off price for the damaged product. If there is no price list for the market, then the Australian price list will apply. If the product is discontinued then we will make a determination based on the last published price list covering the product or for the substituted product, whichever is the lower.
  7. No other repair service will be provided for SPLat boards.
  8. The damaged board must be returned to the original supplier along with pre-payment for the replacement board plus agreed return shipping charges.
  9. All customs duty and/or other government charges will be at the customer's expense.
  10. This policy is made in good faith. If in our opinion boards are being damaged by repeated and avoidable misuse, we will decline further replacements under this policy.
  11. This policy does not apply to SPLat controllers used in applications where damage is likely in normal use, (for example remote control bomb disposal equipment).
  12. This policy does not apply to SPLat controllers that have suffered wear-out of the non-volatile memory areas (permanent and shadow memory). The product documentation clearly states the life-time limitations of such memory.
  13. This policy does not automatically apply to custom SPLat controllers.
  14. This policy is in addition to our 24 month guarantee covering defects in materials and manufacture.
  15. This policy applies to controller boards only unless explicitly listed in a SPLat price list (i.e. accessories, cases and attachments are excluded).
  16. The policy applies only within 5 years of date of manufacture. We may refuse to repair older boards.
  17. This policy does not apply to products that are clearly promote as hobby products, for example EC13.
  18. This policy does not apply to 3rd party products re-sold by us.
  19. This is a policy that we apply at our discretion, not a guarantee.


SPLat educational discount policy

Subject to change without notice

Educational discounts may be granted at our discretion to bona fide secondary and tertiary institutions and to students for use in project work or self study. Commercial or government funded product research is excluded.

This policy applies to direct sales only. There is no obligation on resellers to offer discounts.

Within Australia, New Zealand, UK, USA and Canada: Educational institutions or students buying for self-study

 | Pre-paid with order |  |  | Invoice (Note 1)
Qty |  | Standard product | Hobbled product (note 2) | Standard product | Hobbled product; (note 2)
1-4 |  | 20% | Nil | Nil | N/A
5-9 |  | 25% | 5% | 5% | N/A
10-24 |  | 30% | 10% | 10% | N/A
25+ |  | 40% | 45% | 25% | 35%

Within Australia, New Zealand, UK, USA and Canada: Students buying for use in an official student project:

45% discount off the applicable published list price. Limit 2 controllers per student per year. Students will require written documentation from the institution certifying that the product is genuinely for use in a student project

Note:

  1. We reserve the right to refuse credit. Students must pre-pay.
  2. Hobbled means the board has been modified to reduce its useability outside education (e.g. program storage is volatile). It is otherwise a fully functional unit. We offer less discount for small numbers, more for classroom sets (reflecting extra effort to hobble a few). Hobbling provides security for the educational institution. Trainer boards count as hobbled product.

Other parts of the world:

As above but the discount is not to exceed 35% off. For example, 25 units, prepaid with order, the discount is 35%.


Copyright

This publication remains the copyright property of SPLat Controls Pty Ltd, and may not be reproduced in any manner without our express permission. Permission is hereby granted for you to make electronic and printed copies of all materials except program examples for reasonable use providing no charge is made for supplying those copies to others and providing our copyright and trademarks are noted on the copy. This permission may be revoked without notice.


Trademarks

SPLat® and the SPLat logo are a registered trademark of Microconsultants Pty Ltd.

EasyOne™, EasyStep™, SimpleHMI™, Tabula™, Active Template™, Xwire™, Tiny Serial Peripheral™, FastTrack™, MultiTrack™, MultiTrack+™, Touch of SPice™, SPice™, SPx™, SPiceX™, XPice™, SPLatLink™, SLCom™, reFlash™, PIDassist™ and SPLogger™ are trademarks of SPLat Controls Pty Ltd.

Microsoft Word, Microsoft Office, Windows®, Window 95™, Excel®, Window 98™, Windows XP™, Windows Vista™, Windows NT® and MS-DOS™ are trademarks of the Microsoft Corporation.

SitePlayer is a trademark of Netmedia, Inc.

ViSi-Genie is a trademark of 4D Systems Pty Ltd.


Glossary of Terms

Build

In late 2011 we introduced a new numbering convention for SPLat/PC. The version number now consists of 3 groups of digits mm.dd.bbb.

The first group, mm, is the major version. It changes only on introduction of a very major change.

The second group, dd, is the highest language dialect supported.

The third group, bbb, is the Build number. This number increments whenever we do an inhouse "build" of the program, and can never be repeated. Some builds are developmental, and never get published. Therefore Build uniquely identifies a version, and released build numbers need not be contiguous.

Comment out

To comment out an instruction means to place a comment indicator in front of it so it is no longer operative. Example:

; LoadX 5

Controller

We define this as being the Hardware and the Firmware. This lines up with what we sell you as a standard product or undertake to develop for you as a custom SPLat. We exclude SPLatWare from the definition because that is not something we normally supply.

CPU

In general CPU stands for Central Processing Unit. In relation to SPLat would usually refer to the SPLat virtual processor, and in this context we use CPU as the name of a virtual onboard peripheral that allows access to various properties of the virtual processor. May also refer to the underlying real processor.

Data Memory

See also RAM. This is the old name used for user-accessible read-write memory in a SPLat that is used for storing various items such as counters, floating point variables etc. We are replacing the use of this term with the more universally known "RAM".

Dialect

The SPLat language is constantly evolving and expanding. The very first SPLat controller had an instruction repertoire of 28 instructions. It now has hundreds, and continues to grow. We call successive versions of the the instruction repertoire dialects. A given SPLat board will contain a certain dialect. When it connects to SPLat/PC it reports that dialect, and SPLat/PC will then display the dialect number and constrain you to using instructions within that dialect. SPLat boards with reFlash capability can be upgraded to later dialects as they come out (subject to product-specific constraints).

Enduser

We use this term to denote the ultimate user of the equipment containing a SPLat controller. This would typically be the customer of our customer, where our customer is the company the uses SPLat controllers in their products.

Event

(In the context of event/action (a.k.a. Finite State Machine) programming): An event is something that happens in the real world, say a switch coming on, that triggers the program to take an action. The term may also be used to describe an external condition that already exists, say a switch is already on, when the program enters a given section of code that tests for it. Finally, internal events can exist like a timer expiring or a different MultiTrack Task setting a Semaphore.

(In the context of Tabula): An Event is something that happens outside the control of an Event-Action table that triggers a consequent Action. While the word event implies something of brief duration, like "vase falling", in Tabula the word is often used to describe a steady state condition, like "vase has fallen".

(In the context of SimpleHMI): An event is something (usually a button press) that takes place in the SimpleHMI device (e.g. Android device) resulting in code being sent to the SPLat host. In the SPLat, this can be used to trigger an event handler subroutine.

Firmware

This is a program that is permanently embedded in a processor chip. In a SPLat controller, the processor chip is a regular microcontroller chip that we have programmed to become a SPLat controller chip. That program is the firmware. The SPLat user has no access to the firmware, and the firmware remains our copyright property.

See also SPLatWare, Hardware and reFlash.

Hardware

This is the physical part of the controller, i.e. the electronics.

See also Firmware and Hardware.

Hash command

A type of command in SPLat/PC that simplifies programming of otherwise complicated functionality. In SPLat/PC lines with hash commands start with a hash character, #, in column 1.

Hash function

A special purpose argument type used with a hash command. Written as a function name followed by zero or more arguments enclosed in parenthesis.

HMI

Human-Machine Interface. A term generally used to describe a device that allows a human operator to interact with a controller. Examples are the LCD and buttons on an MS120 controller, industrial touch screens (the most frequent common usage in recent times), or an Android smart phone used with our Android HMI app.

jndexed

jndexing uses the J register to offset (index) a resource address. This is described in the MultiTrack tutorial.

Junction temperature

For a semiconductor device like an integrated circuit (chip) or transistor, this is the temperature of the actual piece of silicon. So long as energy is being dissipated in the component, the junction temperature will always be higher than the surround air (ambient temperature) or the outside surface of the component (case temperature).

LiveData

This was the original name used for our PC to SPLat proprietary communications protocol. It has been replaced by the more descriptive name SPLatLink.

Load

In the context of SPLat Controllers, a load is whatever device is connected to an output. Thus a relay coil is a load. You can also describe the voltage and current of the relay as the load: "The relay presents a 24V 50mA load".

MMi99

The first second generation SPLat controller, with digital I/O, analog I/O, built in operator interface and a new, faster, CPU.

It replaced the MMi88, having all the features of the MMi88 plus expansion connectors and onboard port for an LCD display.

NRE

Non Recurring Engineering or Non Recoverable Engineering cost. This is the initial up-front engineering cost for a new design or project.

NVEM

Non Volatile Extended Memory. A conceptual framework for large non-volatile memories in SPLat. The first implementations are expected in standard products around 3Q04.

Pt100, Pt1000

Platimum temperature detector. The 100 or 1000 refers to the resistance at 0°C.

RAM

Random Access Memory. We used to call it Data Memory, but are changing over to the now widely known term. In a SPLat it is the read/write memory that is used by a SPLatWare program to store variable data.

reFlash

Our term for the technique of loading new operating Firmware into a SPLat board. See reFlash Documentation

Register stack

The main registers of the SPLat processor are arranged as a push down stack of four 8-bit registers. These are named X, Y, Z and T. X is the principal register. The register stack is documented in the Classic Help File, which is part of the SPLat/PC programming software.

RTD

Resistance Temperature Detector. This term is usually applied to platinum temperature sensors, which have a predictable and repeatable positive temperature coefficient of resistance.

SimpleHMI

A simple, easy to use Human-Machine Interface system. SimpleHMI exists as a Android app, as a standalone Windows program and as a feature built into SPLat/PC. A significant feature of SimpleHMI is that there is no need to program the SimpleHMI device (e.g. Android 'phone) for each application. All the application-dependent programming is done in the SPLat (host). SimpleHMI may also be used on non-SPLat hosts, for example hobby-grade boards. SimpleHMI in an Android device communicates with the SPLat via Bluetooth. SimpleHMI in Windows uses a serial (COM) port or USB with a USB-Serial adaptor.

Software

A program that runs in a PC or other non-SPLat computer. This includes SPLat/PC and any special SPLatLink based applications.

SPice

Stands for SP(Lat ) Integral Circuit Element. Symbolises our ability to include application-specific circuitry on a SPLat board.

SPiceX

This is the SPLat expansion scheme that uses the industry SPI bus in its most basic mode. It is suitable for bulk digital I/O expansion with limited programming support.

SPLatLink (formerly LiveData)

A proprietary communications protocol used for communicating between a SPLat controller and a PC. SPLatLink is available in SPLat/PC as a debugging aid, and can also be used in custom PC applications using our SLCom ActiveX control.

SPLatty

SPLatty is the cartoon character who sometimes serves as an anthropomorphised representation of the SPLat processor.

SPLatWare

This is the term used for an application program written in the SPLat language.

See also Software, Firmware and Hardware.

Touch of SPice

Term used for a simple SPLat add-on board that provides some special functionality on a separate board. Derived from the SPice concept, where the special functionality is integrated onto the main controller board, it allows us to offer the lowest possible entry cost into customised control solutions.

Variac

Brand name for variable voltage transformers

WAF

Programmer speak for WAit For. For example, in a comment you might say "WAF tank full"

X

X is the main SPLat register, and part of the register stack. The register stack is documented in the Classic Help File, which is built into SPLat/PC, our programming software.


JavaControl

This web page uses frames, but your browser doesn't support them.