The cio (chained IO) library provides a way to extend the number of available input and output pins almost indefinitely by chaining shift registers. For outputs serial-in parallel-out shift registers are used, for inputs parallel-in serial-out versions. By default six PIC pins are required: a clock, data and a load pin for both the input and for the output chain.
The library first includes ciop which contains the IO pin assignment and various other options. A local copy of this file can be adapted to accomodate a different IO pin assignment.
Output data is shifted out serially, filling the shift registers backward: the data for the most remote register is clocked out first, followed by the data for the other shift registers. When all registers are filled a parallel load is issued which transfers the data to the shift register output pins.
Input data is first loaded, then shifted in serially into the PIC. The data from the shift register nearest to the PIC is clocked in first.
The maximum number of shift registers is limited by the drive capacity of the PIC, clock skew problems (see below) and (often the most important) by the time required to update the full shift register chain. Depending on the load on the PIC pins and the amount of wiring an extra delay between level changes can be required, which increses the time needed to update the data in the shift register chain. Such a delay can be specified in the configuration file.
The schematics and layout should be designed to prevent clock skew problems: line delays and different clock input tresholds could cause the clock to be overtaken by the output data transitions, thus losing bits downstreams. Measures to prevent this can include using the same type of shift register throughout the chain, using a buffer to sharpen the clock edges, or using a shift register like the 74HCT299 which has a delayed output, which practically eliminates clock skew problems.
When the output pins can tolerate glitches during the shifting (for instance when the chain is used to drive LED displays) a cheaper shift register without storage register like the 74HCT164 can be used, or the parallel load inputs of the registers can be activated permanently. In both cases and the load output pin of the PIC can be freed for other purposes.
When the polarities agree the load pins of the input and output chains can be shared, thus saving one pin. Alternatively the clock pins can be shared. Sharing both clock and load pins is also possible, but this requires that the application precedes each shifting in of the inputs by a shifting out of the outputs.
The data out pin and either the clock pins or the load pins can be shared with other uses, for instance the data lines of a HD44780 interface.
The electronic design should take into account that the initial state of the shift register output pins will be unknown.
With the default configuration (no extra delays) and a 16f84 at 10MHz a cio_out_8_load or cio_load_8_in call takes approximately 100 uS (jal V3.0-01).
The configuration file defines whether and input chain and / or an output chain is used, which PIC pins are used, the polarity of the pins, an optional delay between transistions, whether the parallel load are used for output and whether the clock or load pins are shared.
My favourite shift registers are the 8-bits 74HCT595 (output) and 74HCT166 (input). The default configuration file is compatible with these chips. For chips which require a different polarity of data, load or clock signals the configuration file must be adapted.
procedure cio_out_load
procedure cio_out_byte( byte in data )
procedure cio_in_load
procedure cio_in_byte( byte out data )
The cio_out_byte and cio_out_load procedures can be used to fill the output chain (most remote shift regeister first) and load the data to the shift registers output pins.
The cio_in_load and cio_in_byte procedures can be used to load and read the input chain (nearest shift register first).
procedure cio_out_1_load( byte in d1 )
...
procedure cio_out_8_load( byte in d1, ... byte in d8 )
procedure cio_load_1_in( byte out d1 )
...
procedure cio_load_8_in( byte out d1, ... byte out d8 )
One of the cio_out_N_load procedures (N = 1 ... 8) can be used to transfer N data bytes and do the load. The first parameter to a cout_out_N_load procedure is the byte for the shift register nearest to the PIC.
One of the cio_load_N_in procedures can be used to do the load and transfer N data bytes. The first parameter to a cio_load_N_in procedure is the byte for the shift register nearest to the PIC.