/* BigIOoutput.cpp - write serial to parallel output BigIOoutput *PRELIMINARY* Arduino library Copyright (c) 2011 Devon Sean McCullough Licensed under the GPL (GNU Public License) 2011 Aug 02 Tue 17:57 EDT Devon running lights sketch into library HARDWARE Arduino <===> MotherBoard Shield <===> Mama's BIG-IO-output digital outputs to SRCLK, RCLK, SER optional digital outputs normally wired low to ~OE, high to ~SRCLR BUGS scantily tested workaround avr-gcc bug by #undef ARDUINO_CALLOC_ZEROS_MEMORY_PER_POSIX POSSIBLE VARIATIONS replace unsigned long long with a more economical type circular shift register instead of mirror buffer faster multibit set/write http://focus.ti.com/docs/prod/folders/print/sn74hc595.html SN54HC595 SN74HC595 www.ti.com SCLS041H - DECEMBER 1982 - REVISED NOVEMBER 2009 8-BIT SHIFT REGISTERS WITH 3-STATE OUTPUT REGISTERS * 8-Bit Serial-In, Parallel-Out Shift * Wide Operating Voltage Range of 2 V to 6 V * High-Current 3-State Outputs Can Drive Up To 15 LSTTL Loads * Low Power Consumption: 80-µA (Max) Icc * tpd = 13 ns (Typ) * ±6-mA Output Drive at 5 V * Low Input Current: 1 µA (Max) * Shift Register Has Direct Clear SN54HC595...J OR W PACKAGE SN74HC595...D, DB, DW, N, NS, OR PW PACKAGE (TOP VIEW) +-----__-----+ QB | 1 16 | VCC QC | 2 15 | QA QD | 3 14 | SER QE | 4 13 | ~OE QF | 5 12 | RCLK QG | 6 11 | SRCLK QH | 7 10 | ~SRCLR GND | 8 9 | QH' +------------+ http://www.arduino.cc/en/Tutorial/ShiftOut Arduino specific article on this chip. IC Pin TI Phillips 1-7,15 QA-QH Q0-Q7 Parallel Data Out 8 GND GND Ground, Vss 9 QH' Q7' Serial Data Out 10 ~SRCLR ~MR Master Reclear, active low 11 SRCLK SH_CP Shift register clock, leading edge (shift) 12 RCLK ST_CP Storage register clock, leading edge (latch) 13 ~OE ~OE Output enable, active low 14 SER DS Serial Data In 16 Vcc Vcc Positive supply voltage http://www.SamuraiCircuits.com/MotherboardShield Mama Shield socket (top view) +--------+ ARef |] [| V-In +3.3V |] [| Reset A0 |] [| D13 A1 |] [| D12 A2 |] [| D11 A3 |] [| D10 A4 |] [| D9 A5 |] [| D8 Bus1 |] [| D7 Bus2 |] [| D6 GND |] [| GND |========| Bus3 |] [| D5 Bus4 |] [| D4 Bus5 |] [| D3 Bus6 |] [| D2 Bus7 |] [| D1 Bus8 |] [| D0 +5V |] [| +5V +--------+ */ #include "WProgram.h" #include "BigIOoutput.h" BigIOoutput::BigIOoutput(int size, byte *mirror, int ser, int srclk, int rclk, int oe, int srclr) { _size = size; // max bits in shift register #ifdef ARDUINO_CALLOC_ZEROS_MEMORY_PER_POSIX // internal write buffer which storage register will mirror _mirror = (mirror ? mirror : (byte*)calloc(1, BIGIO_BITS_TO_BYTES(size))); #else if (mirror) { _mirror = mirror; } else { int s = BIGIO_BITS_TO_BYTES(size); _mirror = (byte*)malloc(s); while (--s >= 0) _mirror[s] = 0; } #endif _ser = ser; _srclk = srclk; _rclk = rclk; _srclr = srclr; _oe = oe; pinMode(_ser, OUTPUT); // SER serial data out pinMode(_srclk, OUTPUT); // ~SRCLK leading edge shifts digitalWrite(_srclk, LOW); pinMode(_rclk, OUTPUT); // ~RCLK leading edge stores digitalWrite(_rclk, LOW); if (_oe >= 0) { pinMode(_oe, OUTPUT); // optional ~OE (output enable) digitalWrite(_oe, HIGH); // ... initially disabled } if (_srclr >= 0) { pinMode(_srclr, OUTPUT); // optional ~SRCLR (shift register clear) digitalWrite(_srclr, HIGH); // ... initially inactive } } void BigIOoutput::enable(boolean outputs) { // ~OE low: Outputs QA−QH are enabled. // ~OE high: Outputs QA−QH are disabled. digitalWrite(_oe, outputs ? LOW : HIGH); } void BigIOoutput::clear() { // ~SRCLR low: Shift register is cleared. // if optional ~SRCLR not connected try shift(0, ...) // Arduino clock cycle > 20ns @4.5V tw ~SRCLR low min pulse duration // (74HC595 datasheet page 7) therefore no explicit delay digitalWrite(_srclr, LOW); digitalWrite(_srclr, HIGH); } void BigIOoutput::store() { // ~RCLK leading edge: Shift-register data is stored in the storage register. // Arduino clock 16MHz = 62.5ns > 20ns @ 4.5Vcc tw ~RCLK high or low min pulse duration // (74HC595 datasheet page 7) therefore no explicit delay digitalWrite(_rclk, HIGH); digitalWrite(_rclk, LOW); } void BigIOoutput::shift(boolean bit) { BigIOoutput::shift(bit, 1); } void BigIOoutput::shift(BigIO_t bits, int size) { // Low-order bits last to maintain logical-physical pin map over size changes. // ~SRCLK leading edge: Shift-register clock, SER in, other stages over. // Arduino clock 16MHz = 62.5ns > 20ns @ 4.5Vcc tw ~SRCLK high or low min pulse duration // (74HC595 datasheet page 7) therefore no explicit delay // The mask downshift in the second loop is a "logical shift right" // which works with Arduino avr-gcc >> but is not portable - // #define PORTABLE_EVEN_IF_COMPILER_USES_ARITHMETIC_SHIFT // would fix this by masking out arithmetic shift sign propagation. // No sensible compiler would use arithmetic shift anyway // given the more general integer multiply/divide operators, // e.g., arithmetic (value >> count) = (value / (1 << count)) if (!size) return; int oversize = size - BIGIO_BITS; if (oversize > 0) { size = BIGIO_BITS; digitalWrite(_ser, LOW); // extra high-order zeros while (oversize--) { digitalWrite(_srclk, HIGH); digitalWrite(_srclk, LOW); } } BigIO_t mask = 1LL << (size - 1); while (mask) { digitalWrite(_ser, (bits & mask ? HIGH : LOW)); digitalWrite(_srclk, HIGH); digitalWrite(_srclk, LOW); mask >>= 1; #ifdef PORTABLE_EVEN_IF_COMPILER_USES_ARITHMETIC_SHIFT mask &= ~(1LL << BIGIO_BITS - 1); #endif } } void BigIOoutput::shift(byte *buffer, int size) { // Send high-order bits first, low-order bits last. // Little-endian byte index i and bit index j in buffer. // Start with highest-order position (size - 1) and work down. // buffer points to packed binary zero origin little endian bit vector // SER to ~SRCLK settling time presumably needs no explicit delay if (!size) return; int i = (size - 1) / BIGIO_BITS_PER_BYTE; byte j = (size - 1) % BIGIO_BITS_PER_BYTE; byte mask = 1 << j; byte bits = buffer[i]; while (size--) { digitalWrite(_ser, (bits & mask ? HIGH : LOW)); digitalWrite(_srclk, HIGH); digitalWrite(_srclk, LOW); mask >>= 1; if (!mask) { mask = 1 << (BIGIO_BITS_PER_BYTE - 1); bits = buffer[--i]; } } } void BigIOoutput::set(boolean bit, int position) { // set a single bit at position in mirror int i = position / BIGIO_BITS_PER_BYTE; byte j = position % BIGIO_BITS_PER_BYTE; byte mask = 1 << j; if (bit) _mirror[i] |= mask; else _mirror[i] &= ~mask; } void BigIOoutput::set(BigIO_t bits, int size, int position) { // set size bits starting at position in mirror while (size--) { boolean bit = !!(bits & 1); BigIOoutput::set(bit, position); bits >>= 1; position++; } } void BigIOoutput::set(byte *buffer, int size, int position) { // set size bits from buffer starting at position in mirror byte mask = 1; byte bits = *buffer++; while (size--) { boolean bit = !!(bits & mask); BigIOoutput::set(bit, position); position++; mask <<= 1; if (!mask) { mask = 1; bits = *buffer++; } } } void BigIOoutput::set(byte *buffer, int size, int position, int *map) { // set size bits starting at position in mirror from buffer permuted by map while (size--) { int ij = *map++; int i = ij / BIGIO_BITS_PER_BYTE; byte j = ij % BIGIO_BITS_PER_BYTE; byte bits = buffer[i]; byte mask = 1 << j; boolean bit = !!(bits & mask); BigIOoutput::set(bit, position); position++; } } void BigIOoutput::write(boolean bit, int position) { // write a single bit to position BigIOoutput::set(bit, position); BigIOoutput::shift(_mirror, _size); BigIOoutput::store(); } void BigIOoutput::write(BigIO_t bits, int size, int position) { // write size bits to position BigIOoutput::set(bits, size, position); BigIOoutput::shift(_mirror, _size); BigIOoutput::store(); } void BigIOoutput::write(BigIO_t bits, int size, int position, int *map) { // write size bits to position BigIOoutput::set(bits, size, position, map); BigIOoutput::shift(_mirror, _size); BigIOoutput::store(); } void BigIOoutput::write(byte *buffer, int size, int position) { // write size bits from buffer to position BigIOoutput::set(buffer, size, position); BigIOoutput::shift(_mirror, _size); BigIOoutput::store(); } void BigIOoutput::write(byte *buffer, int size, int position, int *map) { // write size bits to position from buffer permuted by map BigIOoutput::set(buffer, size, position, map); BigIOoutput::shift(_mirror, _size); BigIOoutput::store(); } // end BigIOoutput.cpp