Bit-banging or bit-bashing is simply a way to set outputs in a particular order to meet the requirements of whatever is receiving that signal. The main thing about it is that each step along the way is done under program control rather than relying upon hardware assistance to do that.
For example, if a one bit latch latched an input (INP) as an output (OUT) with a positive going clock pulse (CLK) its timing diagram as found in its datasheet might look something like ...
Code:
_________
INP ___| |___ _________________
: __ : : __ :
CLK ___:__| |___:___ ___:__| |___:___
: : : : : : : :
: : : _:___ ___:__:__:_ :
OUT ___:__:__:_| : : : : |_:___
: : : : : : : :
Ts Tp Th Ts Tp Th
Ts (Setup Time), Tp (Pulse Time) and Th (Hold time) will often be specified, but are probably quite small in most cases so can usually be ignored when used with a PICAXE; the time to get to executing the next PICAXE command is usually longer than the time required but not always. We will assume here those times are 1ms, 2ms and 3ms respectively.
So to clock out a bit ...
Set INP high or low
Pause Ts (1ms)
Set CLK high
Pause Tp (2ms)
Set CLK low
Pause Th (3ms )
Set INP low
Converting that to PICAXE code ...
If b0 = 0 Then : Low INP : Else : High INP : End If
Pause 1
High CLK
Pause 2
Low CLK
Pause 3
Low INP
That's bit-banging done. In most cases it will usually be a longer sequence and possibly multiple data lines needing to be set before performing some clocking action. Sometimes there might not be any actual clocking action and only the times between high and low which is important.
The only other thing to bear in mind is how things should be when the program starts. In this case CLK should be set low when the program first starts ( in order to have it rise to do the clocking ) and it may be necessary to add a pull-down resistor on the CLK line to cater for the pin initially being an input for a short while before the PICAXE program runs.
Normally it's nice to have things how a timing digram shows them when entering a sequence and leaving them as shown once done but that's not an absolute. It can help when debugging with a scope or analyser though. In the above example INP doesn't need to be set low at the end because it's set high or low as required before CLK is made high.
As noted earlier, setup, pulse and hold times are usually much shorter than we need worry about so pauses aren't usually required. For a real one bit latch the command sequence could probably be reduced to ...
If b0 = 0 Then : Low INP : Else : High INP : End If
High CLK
Low CLK
Low INP
That "High CLK" then "Low CLK" could also be reduced to a single "PulsOut CLK" with an appropriate time period ...
If b0 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
Low INP
And, as noted, we don't actually have to do that final "Low INP", leaving just ...
If b0 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
That outputs a single bit but if we wanted to sequentially output a set of 8 bits, the byte in b0 sent MSB first, such as to a serial to parallel 74HC595 buffer, we can simply repeat that 8 times ...
If bit7 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
If bit6 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
If bit5 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
If bit4 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
If bit3 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
If bit2 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
If bit1 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
If bit0 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
That's fast but long-winded. If we can tolerate something slower but less memory used, and don't mind losing the original contents of b0 or using an extra b1 variable ...
For b1 = 0 To 7
If bit7 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
b0 = b0 * 2
Next
It might be necessary to output some latching signal (LE) to tell the thing you are controlling it's done so that might need an extra HIGH then LOW or PULSOUT at the end, and there might be a Chip Select (CS) which has to be asserted during the whole thing, so a complete byte write to a 595-style latch could be ...
High CS
For b1 = 0 To 7
If bit7 = 0 Then : Low INP : Else : High INP : End If
PulsOut CLK,1
b0 = b0 * 2
Next
PulsOut LE,1
Low CS
What that would generate, as shown on a logic analyser or scope, is something like ...
Code:
________________________________
CS ___| |___
_______________________
INP ______|D7|D6|D5|D4|D3|D2|D1|D0|_________
CLK ________|__|__|__|__|__|__|__|__________
LE _________________________________|______
Sometimes CS or other signals are inverted, so the HIGH's and LOW's may need swapping as appropriate with signals initialised to the default inactive levels at the start of the program.