Thursday, January 31, 2013

Synchronous Bus for Control Systems

Introduction

This submission describes a serial network shared by multiple nodes using time-division multiple access (TDMA), a relatively straightforward technique for channel-sharing based on repetitive turn-taking. The initial implementation described below involves a network consisting of two or more Microchip Technology "PIC" 8-bit microcontrollers and a Windows PC (emulating an ANSI terminal) communicating at 115,200 baud. During development, this network was informally designated Scrapnet, and this name is used here as well.

Here are some specifics of the network as implemented; note that some of the variations suggested later in the article vary these parameters:
  • Overall channel bandwidth of ~1 megabit/second
  • No possibility of collision between transmitters
  • Predictable service levels at each node 
  • Guaranteed order of delivery
  • Flexible "bus" topology
  • Highly modular, scalable design
  • Compatible with most "serial port" devices
  • Scalable to any number of nodes, subject only to overall electrical and bandwidth limits
  • Ultra-low cost
  • Vertically integrated design
Some features of the PIC firmware written to use this network bear mention as well. Accurate, consistent timing is necessary for TDMA networking. The PICs present on the demonstration bus described by this article achieve this exact timing by running events whose handlers execute with constant latency. In other words, timer "tick" events run at very regularly spaced intervals. There are no locks or critical sections that disable interrupts or otherwise complicate issues of timing. Rather, events run when they are supposed to run, with only a constant lag to deal with. This made the development process more straightforward, and it makes the source code provided easier to modify and enhance; a code base that does not contain, for example, critical sections, does not introduce into the overall system the possibility of errors or inefficiencies related to critical sections.

Equally important, the ability to achieve exact timing pays dividends in areas like cost, performance, and power consumption. In a real-time application, some sort of performance measure must typically be guaranteed, be it by proof, observation, or both. The calculations or observations underlying this guarantee must consider the worst case scenario. It is not usually sufficient for a real-time control system to be fast enough most of the time or only under special circumstances; it must be fast enough even in the worst case, and resources devoted to improving best-case performance are, in some sense, wasted. A design which allows no variation in the timing of interleaved actions or in the latency associated with system events allows for the determination of exact values for throughput, response time, and such. There is no distinction between best case and worst case performance. Performance measures can be achieved exactly, without the need to build in any margin of error, or extra capabilities that are only necessary on a transient or worst-case basis.

Such exactness of effort is not possible with a less deterministic system, e.g. one that allows for collisions between transmitters, or for one node to "starve" another out of its ability to transmit. Under such conditions, the designer must resort to overkill. It may not be possible to guarantee that an Ethernet-based network will transmit a given piece of data within any particular time frame, for example, so designers resort to making the channel so dramatically large in comparison with the data (one hopes) that real-time guarantees can be made on a practical basis. This has its advantages, but it is by no means the most efficient way to achieve (or guarantee) performance goals, and such "overkill" is not the approach taken here.

A single demonstration project, buildable in stages, is provided with the article. This demo application involves two or more PIC 16F690 CPUs operating together on a single main board. This is an exercise in parallel processing which is contrasted with other multi-CPU architectures in the discussion below. In the demo, the 2+ processors share a clock signal and a 115,200 serial data channel, and they construct a composite data stream for an ANSI terminal. The PICs read analog signals from a joystick and a photoresistor, and render a Graphical User Interface (GUI) on the ANSI terminal. Digital input is also read from a pushbutton.

Schematics and wiring diagrams for this demonstration are provided further below. Firmware code for the demonstration is also provided, with "make" and "clean" scripts. All in all, the author has attempted to provide a solid, well-documented beginning suitable for a variety of distributed control systems.

The design described here is designed to be adaptable. Network expansion can be used to add terminals or control stations to an installation, but nodes can also be configured to add hardware redundancy, or even to increase the frame rate of a control system GUI. The techniques and protocols outlined below might be used to build a multi-microcontroller board that is similar in appearance and computing power to a multi-CPU motherboard; but they could also conceivably be applied in a distributed control system spread out over an entire control room.

Flexibility aside, the defining characteristic of the work presented here is its high level of vertical integration. Systems almost universally advertise themselves as "integrated." This nebulous term, though, does not rule out the prospect of selling something that is almost entirely a re-badged integration of other people's work. This is often the route taken by programmers developing application software ("integrated" or otherwise). Even application developers working in relatively low-level languages like C and assembly language necessarily rely on a large proportion of library code, and their designs rely similarly on standard protocols and patterns.

There are advantages, though, to taking one's own metaphorical road when it comes to network architecture. One benefit of handling things in-house (or in a single article) is determinism; developers can better predict the behavior of a system which contains, from their standpoint, fewer "black boxes." This is important for real-time systems and for distributed control systems. It is not as important for many other consumer and business applications. So, devices, protocols, etc. intended for the mass market will often exhibit subtle performance weaknesses in the real-time domain, where simply rebooting the system is not acceptable, and infrequent or difficult-to-find problems cannot be allowed to linger.

Background

Techniques for dividing up a multi-drop serial network have been widespread for some time now. RS-423 is a multi-drop serial protocol that deals with such issues as wire length, voltage, etc.; these are very important for anyone designing a production multi-drop network to consider, but RS-423 does not provide for multiple transmitters, and leaves many issues of protocol unaddressed, especially in the higher levels of the network stack. When networks involving RS-423 devices are built, such details typically end up being proprietary and application-specific rather than standards-based.

This article attempts to bring one such network implementation into the foreground for examination. The techniques described rely on some techniques that are very well-known in a general sense, in particular time-division multiple access (TDMA). This is a term that describes techniques where a communications channel is shared by multiple nodes that take turns transmitting. This is done in a repetitive, circular fashion based around "frames" of a constant time length. Each frame is split up among the network nodes over time in a defined way. Such an approach is fair, simple, and practical, and many networking schemes have employed it, e.g. the iDEN standard used by many BlackBerry-type devices.

The concept of taking turns in this fashion is an intuitive one, but it is by no means the only or even the most prevalent approach to subdiving a digital network. Ethernet, for example, exhibits a more permissive attitude toward channel-sharing: any node can attempt to transmit into the shared channel at any time in an Ethernet network, but it must be capable of detecting any collision and retrying after a pseudo-random period of time. This is a simple and effective approach with unfortunately makes it difficult to establish absolute bounds for network performance.

The implementation provided relies on techniques already explored in its two predecessor articles, RS-232 / RS-423 Communications With Microchip Technology's "PIC" Microcontrollers and Minimalist Floating Point Type. The first of these describes in great detail the basics of setting up a single PIC-based computing device that communicates using a bidirectional asynchronous serial connection. The second of these articles deals with a 16-bit floating point type designed for devices of low computing power, and provides a PIC library built around the type. While this type is not used in the code given here, the parameter stack implementation used in that article is recycled here. The runtime library used by Minimalist Floating Point Type is also recycled here, albeit in more evolved form.

Finally, this latest article necessarily contains a large hardware design component. Another earlier article by the same author, Introduction to Computer Memory Design, With MC6800 DMA Application deals with many of the same issues of hardware design as this article. That article has not been submitted to this site, at least not yet, because it does not contain a software (or firmware) component. It does contain an introduction to digital electronics that some readers may find useful. It also gives a detailed treatment of the diagramming formats (electronic schematic and "rats nest" diagrams) used in the discussion below.

Design

The application described here (and for which the source code is provided here) involves two or more PIC 16F690 processors transmitting on a common link to an ANSI terminal. The PICs take turns sending data onto the shared channel. The resultant ANSI display is a composite of the output of several processors, and the overall body of analog and digital samples (and processor activity) used to build the display is correspondingly increased. More sampling devices, redundant sampling devices, etc. can be added in modular fashion.

Superficially, the resultant installation looks similar to a "dual core" Windows setup; the difference is that the "cores" here, i.e. the PIC processors, do not share memory. Rather than running independent threads-of-execution operating in a common address space, the "cores" run independent processes with isolated address spaces. Any communication must be done using message passing. This is a less flexible design than a "multi-core" system (and intended for a much different target audience), but it is also more predictable in its execution.

The PICs in this design must share a common ground, which is wired to the ground pin of the terminal. They also share a common data bus, upon which each processor has designated "turn" during which it is expected to transmit, if this is necessary. Each "frame" consists of one equal "turn" per transmitter node.

Physically, it is not sufficient to simply wire the output pins of all transmitting PICs to a common bus. The signals must be directed using diodes. In particular, the signal current must not be allowed to pass from one transmitting PIC back up the transmit pin of another transmitting PIC. A diode is a semiconductor which only allows current to pass in one direction, and one of these is inserted between the serial output pin of each transmitting PIC processor and the common bus. In an electrical schematic, a diode is represented by a dark arrow pointing at a bar. Positive current will flow in the direction pointed by the arrow, and diode cases usually have a stripe marked on the side to which current is outputted (i.e. on the ground side).

In addition, two wires are present on the common bus which were not necessary in RS-232 / RS-423 Communications With Microchip Technology's "PIC" Microcontrollers, and both of these relate to the need for exact synchronization. It is easy enough to declare that "a second shall consist of K frames, equally divided among L senders." In attempting to implement such a decision, though, one must answer the questions of (first) what timer or clock signal is used to delineate the frames an (second) when does the first frame start (i.e. the "phasing" of the signal).

The "Scrapnet" design described here uses a common 12 megahertz CPU clock to answer the first question, and a pushbutton "go" switch to answer the second. The GO signal is assumed to be held in the "high" (nominal +5 volt) state by a weak connection to the power source, and to be grounded out by the "go" button (or equivalent). The moment when this signal is grounded constitutes the beginning of the time slice belonging to the lowest-numbered device on the bus. 

Materials

The application was realized using a Radio Shack 6-inch Breadboard (part number 276-002) and compatible jumper wire kit (part number 276-173). This provided ample room for the two PIC processors along with associated jumper wires and a few other pin-through-hole components, which are of the form factor supported by these Radio Shack parts.

The breadboard consists of 63 main rows of holes, each of which is bisected by a long, central groove, creating 63 x 2 groups of five holes each. Each group of five holes is a continuous bus; that is, the holes are connected to each other by a conductor. This main prototyping area is bracketed by four long buses consisting of fifty holes each. Two of these are above the main prototyping area, and two are below. A diagram of the holes present on an empty breadboard of this type is shown below:



The jumper wire kit (Radio Shack part no. 276-173) is closely matched to the breadboard. It contains wires of assorted length which can simply be pushed into the breadboard's holes to make a connection. Two sorts of connections can be made. For a neat layout, the jumper wires can be laid flat against the breadboard, oriented mostly at right angles to its rows of holes. If this is done, and the wires are not crossed over each other, the resultant connections can be translated into signal paths on a single-layer PCB (printed circuit board).

Occasionally, though it will prove expedient to make some long or problematic connection using a traditional jumper wire, i.e. one that protrudes up in the air in messy fashion. These connections will have to remain as jumper wires if the design is translated to a PCB. In the design described below, non-crossing PCB-style paths are used to a great extent, but are augmented by a few loose jumpers.

The 12 megahertz clock oscillator can be any one of many very similar devices designed to drive a computer clock signal. An example is the Abracon ACH 12.0 EK. The actual connection to the terminal's serial port will require something like these "D-Sub" crimp pins. Finally, diodes are necessary. Any silicon diode intended for use with digital (CMOS or TTL) circuitry should suffice; an example is Radio Shack part 276-1102.

To fully utilize the demonstration firmware provided will also require a photoresistor (e.g., Perkin-Elmer part no. VT90N2), a 4750 ohm resistor, and a two-axis analog joystick (e.g., Radio Shack part no. 26-3012B or any number of similar units).

Protocol

The foundations of the firmware code presented in RS-232 / RS-423 Communications With Microchip Technology's "PIC" Microcontrollers are once again present in this latest design. In particular, the PIC's serial device is placed in asynchronous mode, and inverted signal polarity is selected. This results in a waveform which is readily interpretable by most "serial" or "RS-232 compatible" devices, although it is technically a 0-5 volt waveform in the shape of an RS-232 waveform. A baud rate of 115,200 bps is configured. Not incidentally, this is the highest rate mentioned in the actual RS-232 standard.
Each second of network time is divided into approximately 6 frames. To be exact, there are


frames per second, and each frame is divided up evenly among the network nodes starting with node 0, followed by node 1, and so on. In an application where the overall waveform on the Scrapnet data channel is interpreted as a stream of data for a single ANSI-compatible display, the ~6fps rate ends up being not only a network frame rate, but a constant screen frame rate as well. This is exactly how the demonstration application documented in this submission operates.

These numbers are extrapolated upon in detail below; for now, note that the Scrapnet protocol uses a CLOCK signal which is a square wave having 12,000,000 "teeth" per second. Further, consider that 221 is a cycle count which is relatively convenient for digital hardware to calculate, since it is an even power of two.

It is reasonable to ask about the relationship between the frame rate and the baud rate. Similarly, there is the question of how the CLOCK waveform (a repetitive square wave) relates to the transmission waveform (a similar square wave). The apparent conflict between what seems to be a fundamentally synchronous network (viz. the CLOCK signal) and the asynchronous mode selected by the PIC firmware provided is related to these questions.

In clarifying such design points, more flexibility is allowed than is allowed in defining the relationship between the CLOCK signal and the frame rate. Each node is allowed to utilize its portion of each frame freely, subject only to the stipulation that each node's transmission must be a serial waveform whose structure (and, most specifically, whose framing) is some function of the CLOCK signal. This function must hold true for the entire network. So, it is allowable for each node to (as is done here) fill its turns on the network with a transmission of approximately 115.2 kilobaud, having 8 data bits, no parity bits, and one stop bit. The specifics, though, can vary. The baud rate need not be 115,200 if, for example, attached devices do not support it, or if cable run lengths mandate a slower rate.

However, to the extent that there is deviation from the nominal baud rate, it must be constant across the whole network. It is not allowable for, for example, one node to transmit at 115,000 baud and another at 115,400 for a given real CLOCK signal; this is what is meant when it is decreed that the waveform comprising each frame must be some function of the clock rate.

However, note that two nodes transmitting at, for example, 115,000 baud can absolutely constitute a Scrapnet network, and this network can quite likely interface with terminal equipment operating at a nominal 115.2 kilobaud. In all these cases, there is of course some tolerance; to state otherwise is not realistic. However, the intent of the specification is that the framing of each node's portion of the signal ought to be consistent across the whole network, and ought to vary predictably as a function of the real clock rate.

This guideline is designed to ensure compatibility with a broad range of "serial port" or "RS-232 compatible" devices. Many of these devices will operate in asynchronous mode; that is, they will not make any use of the CLOCK or GOsignals. In fact, this is true of the initial Scrapnet demonstration presented below. Such implementations rely on a consistent square wave to determine the phasing and framing of the signal. Disallowing any nominal variance between the network transmitters in these areas, as is done here, ensures that ordinary asynchronous equipment can be easily connected.

Note that when baud rate varies from 115,200, the frame rate is still held constant. In the underlying PIC code, the setup code for the UART under such implementations will differ from the code provided, but the timer setup code and the interrupt handler can be identical. A wide variety of baud rates were exercised during testing, especially 9,600 baud and 19,200 baud. RS-232 / RS-423 Communications With Microchip Technology's "PIC" Microcontrollers employed a 57.6 kilobaud channel, and this rate was tested with Scrapnet as well, with good results.

Firmware

The PIC firmware code provided relies on a modular runtime library already largely exposed in Minimalist Floating Point Type. In the file names and identifiers used here and in the floating point article, this library is referred to as HLOE (High-Level Operating Environment). HLOE allocates two parameter stacks in the CPU's SRAM file, which are operated upon using macros declared in "kernel.inc" (PUSH and POP for stack 0 and HLKRNPSH and HLKRNPOP for stack 1). Stack 0 serves are the parameter stack for HLOE library functions, as well as for what amount to user-level functions in "target.asm." Stack 0 also holds automatic variables during function execution. In the code provided here, stack 1 is used chiefly to hold base pointers into stack 0 during each parameterized function call.

HLOE library "kernel.asm" contains the most basic functions built around these stacks (e.g. integer math and byte-level serial I/O). The SFP libraries around which Minimalist Floating Point Type was built are not used in this latest demonstration, which uses only integer math. However, "kernel.asm" is shared with Minimalist Floating Point Type, especially the I/O subsystem. Such selective consumption of libraries is facilitated by the inherently modular nature of the runtime; future articles will likely explore this topic in its own right. Finally, many other "HLOE" conventions are shared with Minimalist Floating Point Type, e.g. the use of many relocatable code segments and the resultant need to use the FAR_CALL macro, which brackets each call in bank selection operations.

In the demonstration, once the PIC is configured and its 16-bit timer is started, the main task enters a busy loop and simply allows timer events to occur. Before any of this happens, though, the main task waits for the GO signal to get grounded out, and it also applies a staggered delay based on each node's position in the network, or "station number." This staggered delay, in conjunction with the use of a common CPU clock and predictable timer events, establishes a well-framed and well-phased composite waveform on the common channel. A 16-bit timer is used because the necessary period (assuming the CPU sends one frame per timer event) is too long for any of the 8-bit timers.

As hinted at in the last section 221 (from the frame rate calculation) is intended to be a number that is relatively easy for Scrapnet-connected devices to count with fidelity. In the case of the PIC 16F690 used here, a timer capable of counting up to 216 (the "16-bit timer") is available. This timer is incremented with every instruction cycle, and an instruction cycle occurs every four (22) clock cycles. A prescaler is also provided by the PIC, which can be used to slow the timer count. In the demo firmware, this is set up to slow the count by a factor of eight (23).

If the 12mhz Scrapnet CLOCK is used by the PICs as their main CPU clock signal, then, the correct timing will be achieved. Each timer event will occur with every 216+2+3 = 221 cycles of the CPU clock, which is the same as the network CLOCK signal. The frame rate, in this implementation, is thus:





One high-level technique that is evident in the code base provided, but not in Minimalist Floating Point Type, is the use of setbit and clearbit functions to set up bit fields. In writing the startup firmware necessary to initialize the PICs and their peripherals, several techniques of varying effectiveness are available to deal with the various bit fields that need to be edited.

One easy but potentially problematic technique is to simply execute BSF and BCF instructions in sequence to set a configuration byte one bit at a time. This places the configuration byte into several intermediate states of indeterminate validity, though. Another option is to simply write a constant byte to the configuration register in a single instruction. However, this second technique does not allow for the possibility of leaving certain bits unchanged. Also, even if the programmer can determine correct values for all 8 bits, his time may be required elsewhere.

The approach used here (setbit() and clearbit()) attempts to combine the legibility and flexibility of using BSFand BCF instructions for bit field configuration with the determinism achieved by using a single, 8-bit instruction. Thesetbit and clearbit functions expect two byte parameters atop the stack. The top of these is the byte to be manipulated, and the next parameter is a bit number (0-7). The top byte has the bit designated by the other parameter set or cleared, as appropriate, and the result is then returned at the top of the parameter stack.

Several such calls can be nested to effect a series of configuration byte bit operations without committing the result to the actual configuration register until it is completely well-formed. Consider the following statement, rendered here in C, which describes the setup of the T1CON register:
T1CON=clearbit(TMR1CS clearbit(TMR1GE setbit(T1CKPS1 setbit(T1CKPS0 T1CON))));

In English, this means to take a copy of the contents of static location T1CON, set bit T1CKPS0, set bit T1CKPS1, clear bit TMR1GE, clear bit TMR1CS, and put the result back into static location T1CON. A relatively simple expression evaluator (e.g. within a cross-compiler) using a stack-based algorithm can determine the combination of PUSHoperations, POP operations, and function calls to evaluate such expressions.

In fact, the code used to set up T1CON for this demonstration was generated from the expression shown above using an expression evaluation tool based on Dijkstra's Shunting Yard Algorithm, although this code is not provided or discussed here. Such techniques may comprise part of a follow-up article.

However, the implementation of the expression shown above is fairly intuitive even without a solid understanding of expression evaluators. As it turns out, expressions of the general class shown above translate to object code in rather formulaic fashion: the bit number constants must be pushed from left to right. Then, the initial register value is placed atop the stack.

Finally, the function calls are executed from right to left. So, in this case, we find the following assembly language code near the top of the main task (in "target.asm"):
 ;Set up T1CON

 movlw TMR1CS  ; Use CPU clock

 PUSH

 movlw TMR1GE  ; No gating of timer

 PUSH

 movlw T1CKPS1 ; 8:1 prescaler

 PUSH

 movlw T1CKPS0 ; 8:1 prescaler

 PUSH

 banksel T1CON ; Start with current T1CON

 movfw T1CON

 PUSH

 FAR_CALL hlluserprog,setbit

 FAR_CALL hlluserprog,setbit

 FAR_CALL hlluserprog,clearbit

 FAR_CALL hlluserprog,clearbit

 POP

 banksel T1CON ; Save new T1CON

 movwf T1CON

Finally, one more specific of the firmware design used here ought to be noted: it executes, after the start of network traffic, with interrupts enabled at all times. There are no locks or critical sections, for example. Anything less would result in a variable interrupt latency, and in uneven spacing of timer tick events, or jitter. This would further introduce worst-case performance as a distinct measure, with all of the negative economic implications discussed above.

This lock-free design is easy enough to achieve in the relatively simple demo given here. Basically everything of substance, at least after the "go" button is pushed, happens within the ISR. The main task does basically nothing at this point, and there is no real possibility of interference between it and the ISR.

However, the full HLOE system contains additional features, not fully explored or even provided here, which facilitate such a jitter-free, constant-latency environment. The twin stacks provide the foundation of this jitter-free real-time environment. By enabling the use of automatic variables and parameters versus statics and globals, for example, the HLOE stacks eliminate any need to arbitrate access to static variables, e.g. using locks. (This is a classic benefit ofFunctional Programming).

In order for this to work, it is necessary for the stack implementations themselves to be interrupt-capable. The implementations supplied fulfil this goal, mostly by ordering operations carefully. Also, it is helpful here that truly independent stacks need not be provided for the interrupt service routine (ISR) and the main task, even in a full-featured HLOE application. When it interrupts the main task, the ISR simply builds the stacks it needs atop the same stacks used by the main task. When the ISR returns to the main task, it replaces the stack pointers where they were. The ISR must not POP off more values than it has PUSHed, at any given time, but in a well-formed program it will not have reason to. The stack values beneath the ISR's own stacks are unpredictable in value anyway.

The full implications of these design decisions are not within the scope of this article, and it must also be mentioned that the HLOE library here contains some optimizations (and, in particular, simple omissions for the sake of brevity) which mitigate some of these benefits. (Readers who find this problematic should contact the author or wait for the follow-up article.) Despite these omissions, though, the code seen here is very typical of high-level HLOE code as the author envisions it, and the model of parallelism described in brief above has been an effective one so far.

Hardware

The physical setup described in RS-232 / RS-423 Communications With Microchip Technology's "PIC" Microcontrollerscan run either half of the demonstration code; that is, it can emulate node 0 or node 1 on a standalone basis. The programming techniques necessary here are also identical to those described in that article. As in that submission, a"PICKit 2" programmer is recommended, and a program included with the programmer can be used to perform the actual programming. For the complete demo, of course, at least two rounds of building / programming will be necessary, one for each CPU.

.HEX file is included with the download package that is suitable for a single node of the multi-node demonstration. To run the full demonstration requires .HEX files for each node to be built. This process is automated using the provided script "MAKE.BAT" (which assumes that MPASM is installed under "C:\Program Files\"). Prior to running this script, two compile-time constants that define the network must be edited. These are present in "TARGET.ASM" near the top of the file. Constant STATNO is the station (node) number (0 or 1 for the two-chip demo) and STATIONS, the initial station count, is 2 in the demonstration described but can be raised higher; this possibility is discussed further below.

To fully run the demonstration requires not just building and programming two PICs, but also the construction of a twin-PIC board, e.g. using the Radio Shack "breadboard" and the associated parts described in the "Materials" section above.

A schematic for the board is shown below this paragraph, using the customary symbology. Note that an understanding of this sort of diagram is not necessarily a prerequisite for use of the materials provided here; more detailed wiring diagrams are provided just a bit further below, along with a quick overview of the rationale behind the connections being made.

These materials should allow anyone with sufficient patience to assemble the demonstration components properly. Also, the articles Introduction to Computer Memory Design, With MC6800 DMA Applicationand RS-232 / RS-423 Communications With Microchip Technology's "PIC" Microcontrollers are recommended for readers who wish to expand their understanding of diagrams like this one, and the thought process that underlies their development.

Much of what is shown in the schematic above has already been described. Actual pin numbers and an additional part number are given above. Also, some diodes related to the GO signal pushbutton are shown; again, the purpose of these is to shunt signals in the direction anticipated by the design, and to prevent cross-talk between the CPUs.

The programming circuit itself is not shown in the schematic above, for the sake of simplicity. Similarly, the circuitry involving the photoresistor and the joystick are not shown until later.
A second version of this schematic is shown below. Here, the core network bus (versus portions that are specific to each CPU) are highlighted in red:


(The channel labeled COM is the "common" or "ground" wire.)

These schematics do not really describe how to wire the demonstration board using the breadboard, and this is true even for the experienced constructor. A more detailed description of how the author laid out the actual board is given in the next section, which puts forth a detailed wiring diagram for the demo, in stages. Additional details not present in the schematic are added to the system in these diagrams. One example is dual programming harnesses. These are not strictly necessary just to get the demo to work. After all, the CPUs can be programmed while inserted into the Microchip Technology "Low Pin Count" demo board or something similar. To do intensive development with this setup, though, would require too much device insertion and extraction to be practical. The dual-harness setup shown in the wiring diagrams below allows in-place programming of the PIC devices.

Setup

A reader in possession of the diagram shown above, and of the Radio Shack prototyping parts already listed, should not have too much difficulty constructing the demonstration board. Note that the jumper wire colors used in the diagram are intended to match the actual colors used by Radio Shack, reducing the trial-and-error necessary for the reader. It is advisable to first place the integrated circuits, then the jumper wires that lay flat against the breadboard, and then the other messier pieces of the assembly (true jumper wires, programming harness, and such).

The next few diagrams presented are wiring diagrams projected atop a symbolic representation of the Radio Shack 6" breadboard. All of these use a common symbols chart, which is inserted below:

The remainder of this section describes in detail how to physically assemble the demonstration hardware. This is done in steps which follow the basic order outlined above. To begin, a good first step toward building the demo is shown in the diagram below the next paragraph. Note that this diagram, like the rest of the diagrams in this series, is a close-up of the relevant area of the breadboard. After placing the components shown, the reader will have a two-PIC installation which can at least be powered up (after all the components are in place) and programmed.

Each of the two programming harnesses consists simply of jumper wires, in the U-like shape in which they are supplied, with one side plugged into the breadboard and the other side hanging over the long edge of the breadboard. If the colors shown are used, these wires can be aligned easily for insertion into (for example) a PICKit 2 programmer. Pin "1" must be oriented as shown, and this will leave one pin of the PICKit 2 unused. Also, this configuration will place the programmer's bottom side facing outward, and its interface facing upward and exposing its holes in basically the same plane as the breadboard. The other wires in this diagram can all also be left in their "U" production shape, and pressed flat against the breadboard. 


The next stage of the construction process adds the clock oscillator and related circuitry. In the diagram shown below this paragraph, the components added in the last stage are dimmed compared to the oscillator circuit. The rest of this series of diagrams all use this convention. Below, note that each PIC accepts a CPU clock signal (5 volt square wave) at pin 2. The clock oscillator requires connections to power and ground, in addition to exposing an output pin. This device also has a unique shape, and this can be discerned from a careful examination of the diagram below and used to position this device properly:



The next wiring diagram adds the GO signal and associated pushbutton switch. The switch connects pin 3 of each CPU to ground, when the pushbutton is depressed. The provided firmware code polls the relevant digital input (A4) until it is grounded out by the pushbutton.

A "pull-up" resistor holds the value of the GO signal high (5V) at all other times. The resistor slows the flow of current from the power source to the PIC. If a simple wire were used to hold the signal high at the CPU, it would overload this device with current. The resistor used should have a fairly high resistance rating (e.g. 4750 ohms). The CMOS/TTL equipment used here needs only a weak connection to detect a high signal. Diodes are used for the first time in this stage. In performing the physical installation of the diodes, orient each diode such that its case stripe is on the side opposite from the lead connecting to the CPU. This allows current to flow from the CPU toward an eventual connection to ground. Finally, note that this stage of the setup process is also the first in which a "messy" jumper wire, i.e., a long one that does not lie flat against the breadboard, must be used. This is done for the large, central yellow jumper wire.



The stage documented in the next diagram, beneath this paragraph, sets up the DATA line of the Scrapnet bus. The terminal pin numbers provided assume a "DB-9" serial port connection on the terminal, e.g. a PC "serial port." Again, diodes are used to guide the signal, in particular to prevent one PIC's transmission current from going up the transmit pin of another PIC. Also, note that another "messy" or "true" jumper wire is used in this stage (the long red line in the image below). The physical connection to the terminal can (assuming a "male" DB-9 or DB-25 is present) be constructed using long jumper wires and "D-Sub Crimp Pins". Transmission requires a two wire connection: one for the 0-5V DATA square wave, and a common ground wire to provide context for the receiver and allow current to actually flow.


Demonstration

At this point, enough of the board has been built for a meaningful demonstration to take place. The output of the demonstration is a bar graph-based GUI presenting measurements of analog signals. Nothing has yet been connected to provide these signals, but all of the other communications and infrastructural hardware has been connected. It is possible, at this point, to program the two CPUs, apply power, and receive a serial transmission on the terminal.

When power is applied to the demo board (but before the pushbutton is depressed), the commands necessary to display a "desktop," which consists of a low-contrast repetitive test pattern rendered in the black/green "night mode" palette, are transmitted. If an ANSI terminal is connected, it will display something like this:



After the button is pushed, the assembly will begin transmitting a composite stream of serial data to the terminal(s), which will be constructed by the CPUs operating in parallel and will update the screen at the designated frame rate. Some bar graphs will be shown atop the desktop, and these graphs at this point will reflect analog signals that are "floating," i.e., unpredictable and unconnected. Typically, a low or zero value will be indicated for each signal. Finally, note that pushing the "go" button during operation will cause an asterisk character to be displayed near the lower right-hand corner of the GUI. An example of this terminal-based GUI in action is shown below:



In this last picture, note that the right side bar graphs (which move in tandem) reflect the current value of analog inputA0 on the right-side PIC (the second processor, where STATNO equals 1) and the left side bar graphs reflect the value of the same signal on the left CPU (where STATNO equals 0).

Day/Night Mode

The value of A0 on CPU "0" is assumed to come from a light meter (photoresistor), and is used to determine which of two palettes (day or night) should be used in constructing the ANSI display. Processor "0" is thus the only CPU that emits the ANSI commands that change palette, and a consistent "day" or "night" mode is thus displayed on all terminals, without the need for control by the end user. The "day mode" GUI is pictured in the next image. Note that the left side bar graphs are near their maxima, which reflects the high-infrared light source applied to force "day mode".



The A0 channel on processor "1" connects to the joystick signal, specifically to the signal for its "X" (left-to-right) axis. This is an analog signal which ranges from approximately 0 volts DC to 5 volts DC. The right bar graph thus moves in rough correspondence with the joystick handle in the left-to-right axis.

Together, this section and the one after it describe how to connect the photoresistor circuit and the joystick circuit, thus completing the demo provided with this article. However, another section after these sections discusses further ways to expand the network described. These possibilities extend well beyond the relatively simple demo given here.

In an installation environment with sufficiently high ambient light, such as a vehicle cabin, control tower, or guard tower, it will be necessary to alter the visual user interface presented to allow for variations in time and weather. The full illumination necessary in high sun operations is usually not suitable (i.e. is blinding) under conditions of low ambient light. Correspondingly, the low level of illumination appropriate for nighttime use will likely be invisible to the user under high sun.

Generally, the systems created to deal with such conditions alter the illumination (brightness and contrast) of the man/machine interface across some range. It is usually considered desirable for the shifts between the various dimming modes to be executed consistently across the network / installation. One example of such a setup is the dashboard dimmer knob present in a simple automotive control suite. While many day/night setups rely, like the auto dimmer knob, on user input, this is increasingly becoming obsolete. Instead, a photocell of some sort can be used to automatically control day-versus-night mode.

The demo system described here relies on a binary day/night mode controlled by a single photoresistor, and rendered by commands issued from a single CPU. Accordingly, a photoresistor is wired to CPU "0" (specifically to its A0 signal, on pin 12). As was seen in the "go" button circuit, a "pull-up" resistor is necessary to ensure a reliable signal. Specifically, it is necessary to avoid a condition known as a "floating" signal, where the sensor resistance is so high (e.g. in the pitch black) that without the pull-up the CPU signal pin would basically be connected to nothing, resulting in unpredictable sample values. The image shown below presents the additions to the circuit associated with the photoresistor, using the same format as the other incremental wiring diagrams already shown:



A few words are in order, at this point, about the relative merits of the day/night mode system outlined here. The author has observed a wide variety of such day/night systems in operation, and has even been involved in the development of several. These range from the simplest, manual knob-based devices to sophisticated control systems running on ruggedized general-purpose computers. Unfortunately, many of these systems are vastly inferior to the Scrapnet-based system described here. Many require human intervention, in the form of a button push, knob twist, or GUI action. Some Windows-based systems manipulate the system palette in proprietary ways; this limits the ability of the computer (designed and priced as a multi-application system) to run other applications. The design given here (constructed with the benefit of hindsight) avoids such pitfalls.

Joystick

The joystick in the demo application simply drives the position of the bar graphs shown on the right side of the display. While a part number is given in the "Materials" section near the top of this article, which references the actual joystick used by the author, most analog joysticks should suffice for the demo.

Each axis of the analog joystick is electrically a simple potentiometer. This is an electrical component which takes a voltage source provided to it on power and ground connectors, and transmits some fraction thereof onto a third connector. In this application, the joystick connection is on the opposite side of the board from the peripheral interfaces described thus far. To facilitate this layout, a 5-volt bus above the rest of the demo circuitry is wired up. This is used to provide the joystick input (and potentially other power needs). Again, a pull-up resistor is connected to the PIC's sensor pin alongside the connection to the joystick. This is present to ensure a constant baseline for the signal, which might otherwise float. Like the joystick signal, this pull-up resistor is connected to the new, top-level power bus. The wiring diagram below shows the additional circuitry necessary for the joystick:



In the interest of completeness, a wiring diagram for the whole demonstration is shown below. This is a 600-pixel-wide image, in order to reduce the demands placed upon the host site. A larger image is available upon request.



Expansion

One fairly straightforward way to expand the network is to increase the number of PICs. This is supported in the code by way of the STATIONS constant in "target.asm." The size of the time slice, and the initial delay used to properly phase the nodes' transmissions, are both affected by this constant. Some of the calculations associated with this constant are approximations in the code supplied (and this is noted in the code comments), but even so some successful tests with STATIONS set to 8 have been performed.

The real limit on the number of stations on an optimally configured Scrapnet bus is undoubtedly much higher. An example of a four-PIC Scrapnet bus is shown in the schematic below, which attempts to give an idea of the pattern followed by such expansions. Again, this is a 600-pixel-wide image, in order to reduce the demands placed upon the host site. A larger image is available upon request.



Such additions to the network can serve several goals. In the demo presented here, the two PICs split up the terminal screen and each take responsibility for one half of the GUI. But there is no reason two PICs cannot be configured to draw the same portion of the GUI, provided each respects its place in the network frame timing. This can provide redundancy. Processor 1 in the demo could, for example, be given its own connection to the photoresistor (or another photoresistor), and its firmware altered to draw the same left-side bar graphs as CPU 0. Then, the same signal (ambient light) would be presented at approximately 12 frames-per-second instead of 6. If one CPU were to fail (or its firmware to crash), the other would keep updating the GUI at 6 frames per second.

As was mentioned earlier, one must consider electrical issues like cable length and signal power when adding nodes to a network. This is especially important in a final production application. To a great extent, though, the bus described here is intended to be scalable from an electrical standpoint, just like it is from a firmware design standpoint. Each PIC CPU added to a Scrapnet network dissipates a quantity of power to the network which is relatively constant; consider that the idle signal is high (5V) when SCKP is set, and most PICs will be in this state most of the time.

From a voltage standpoint, this is not a problem. Because the Scrapnet design connects the PICs in parallel with each other, this additional power contributes to the overall signal current (i.e. its volume), not its voltage (i.e. the amplitude of the signal wave). So, a circuit of, for instance, 5 PICs, could in theory dissipate 5 times the power of a single-PIC circuit, but this would be done by increasing the current of the signal, leaving its voltage unchanged. Otherwise, even a relatively small network would overwhelm the positive voltage limits of a typical serial port.

It is also important to consider the current dissipated onto the DATA channel. Because the "idle" or "mark" state is high (5 volts), a Scrapnet bus will operate at near maximum current much of the time. In general, though, RS-232 and similar standards specify much larger overall power levels than the inverted TTL waveform actually sent by each CPU, and this buys the designer a certain amount practical leeway in stringing together larger CPU groupings. Beyond that, the presence of a diode at each processor's transmitter pin prevents even an overloaded DATA channel from damaging these devices through that pin. So long as receiver devices treat the DATA channel as tantamount to an RS-232 channel (as is done in all the demonstrations and suggestions presented thus far), then electrical overload is not likely to occur at the receive pin, either.

Another potential direction for expansion, beyond adding nodes, is to make better use of the PIC processors already present in the demo circuit. These are each capable of doing much more processing, and of taking many more samples, than is explored in the demo. Only limitations of space and scope prevent such full utilization in this first article.

Finally, two-way communication on the Scrapnet bus is not demonstrated here. However, some general guidelines can be given. Because the SCKP bit is set in the demonstration firmware, an inverse TTL waveform is generated by each PIC's UART. This setting does not alter the expectations of the UART's receiver, though, and the Scrapnet DATA signal is thus an inverted version of what this same PIC expects to receive. For this reason, any setup in which a single PIC must both transmit and receive will most probably require additional hardware. Maxim's MAX232 or a similar device can likely perform the signal inversion that is necessary. This has not yet been tested, though.

Conclusion

In most high-level design processes, there is some need to determine the appropriate level of abstraction at which the development process should operate. This question is often couched in debates over things like tool selection or outsourcing. Ultimately, though, the design process must at some point decide which portions of the system are to be developed in-house, and which portions are to remain "black boxes" to the development team.

It is hoped that this article lends support to the idea that it can be feasible, even beneficial, to operate at a relatively low level of abstraction. A little bit of hands-on knowledge can translate into lower cost, fewer support calls, and improvements in reliability and ultimately even safety. Abstraction can be good, but it is not an absolute good; the system developer has an obligation to understand the whole product of his or her work, and this is not always compatible with working at the highest level of abstraction possible.

Setting aside such general considerations, it is further hoped that readers will appreciate the specific capabilities of the demo circuit described above, which assembles a few dollars' worth of parts into a computing device of some power and utility. The PICs are remarkable devices, and the capabilities of even the lower-end models are formidable.

Finally, the author hopes that this submission will help disseminate, in some small way, some valuable higher-level PIC programming techniques. The use of dual stacks and of a functional programming perspective are not typical of code for the 8-bit PICs in general. Certain aspects of the PICs' economical design seem to discourage such techniques. These include very limited random-access storage, and the availability of just one index register. Nevertheless, the firmware presented here shows that such high-level idioms are not at all impossible on these devices. In fact, designs including such techniques can still achieve the predictability and performance for which the PICs are well-regarded.

History

This is the third release of the article. In both updates, some explanatory passages have been improved. The technical content of the article remains the same. 

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)