ICD2 isolator

Microchip's ICD2 is a handy programmer, even when not using the in-circuit
debug facility, as it can be set up to automatically program a device after a successful
build, providing a very quick "assemble-burn-run" cycle time for small projects
where you can live without an emulator or debug facilities, e.g. on parts that don't
support ICD.
One problem however, is that the ICD2 loads the RB6/7 lines significantly, which can be an
issue for some designs.
To get around this I built this simple isolator, which plugs inline with the ICD2 cable,
and disconnects RB6/7 using a relay when ICD2 is not programming the device. The relay is
turned on when the MCLR line is either at 0V (PIC held in reset by ICD) or >8V
(Programming mode) A 3V relay was used to allow operation over the whole supply voltage
range.

Note that this is only suitable for use when the ICD2 is being used as a
simple programmer, not when using the ICD debug facilities.

This works very well, however if building it again I would use a 4 pole
relay, to allow :
1) MCLR to be disconnected from the target - this is useful when using parts that can use
the MCLR pin as an input, as ICD2 drives the MCLR pin. in the meantime, a 1K series
resistor does not seem to affect programming and did the job when I needed it.

2) Forcing of the target Vdd to 5V during programming for designs running
at 3V - this can speed up programming significantly as the Bulk Erase instruction can only
be used above 4.5V.

Now that self-programming is available on some smaller PICs (e.g. 16F818),
I was thinking about ways of providing in-system firmware updates with the absolute
minimum of code overhead - a typical 'traditional' bootloder takes of the order of 128-256
words, depending on the communications requirements, which is a significant chunk on a 1K
part.

I was thinking particularly about a current design, but it would be applicable to any
system where the main application already has :
a) An external eprom or serial flash etc. memory big enough to hold a code image
b) Some communications link capable of reading/writing to the eeprom.

Instead of the traditional bootloader approach (where the loader handles the comms and the
programming, and has to be able to recover if the comms fail, you copy the code into
eeprom using the existing application code, verify it back, then call a small routine at
the top of memory to
copy the eeprom image to the flash. This has the advantage that because you already have
the comms code in your main application, the only code overhead is the actual
eeprom-to-flash copying code.

Another advantage for this method could be where the available
communication protocol is too complex to fit economically into a bootloader, e.g. TCP/IP,
USB, Zigbee etc. In this situation, the cost of adding an extra eeprom to allow code
updates may well be justifiable.

It eliminates the need for a bootloader to be able to recover from comms failure, as you
can write and verify the data before committing to the flash programming operation. The
only hazard would be a power failure during the copying process, a much less likely
occurrance.
Depending on how much of a risk you consider this is for your product (and how difficult
it would be to recover from a failure), you could trade off a bit of code space by having
the copy routine rewrite the reset vector to restart the process if interrupted, copying
the 'normal' vector back as the last operation, narrowing the window time in which a power
failure would be fatal to an insignificantly small few tens of milliseconds.
Alternatively (at the expense of a little more code) it could be set up to always boot
into the copy code, which would check the eeprom to look for a flag to signal a new image
to be loaded.

For devices like serial flash on hardware SPI, where one command can read a large number
of data bytes, the main application code could even initiate the eeprom read command,
eeprom address etc., so the copy code would just have to read the stream of bytes from the
SPI port, giving an extremely
low code overhead.

A limit on the sizing of the 'resident' eeprom copying routine is the 32 word row
erasability boundaries, so if the code can't be squeezed below 32 words, there is no
benefit in using any less than 64, so this could be used to add some of the various
'protection' options I outlined above, or to move the setup code into the resident area to
free space in the application area.
My first attempt put the resident part at 37 words, so i just HAD to do some squeezing to
get it down to one row, just to prove it could be done...!

The total code overhead for enabling the in-system reprogrammability came to 50 (decimal!)
words, 18 words setup within the application code and 32 for the redident copy code which
lives in the top row of program space.

Having all the setup within the application code area instead of the fixed 'resident'
section maximises the possibility for future code squeezing/sharing if things start to get
really tight, but care must be taken to ensure that it will work correctly in all
circumstances. Flash-program time for the 16F818 is about 0.7 seconds, so even without any
additional protection mechanisms, the 'potential disaster' window is pretty short.

Note that this technique is only seriously advantageous for systems that ALREADY HAVE a
suitable external eeprom/serial flash, and
communications system as part of the main application.

here's the code - obviously will need tweaking to suit other apps, but the 'get byte' is a
seperate routine and should be fairly easy to modify (once you understand the strange bits
done to save space..!)

; the exit address would typically be a well-defined, non-movable address in the app.code,
e.g.0002
; an alternative would be to use "goto $" to loop infinitely and force a reset
when the watchdog
; times out.

sspdelay ; entry point used for SPI wait
delay
goto $+1 ; long NOP, used to extend ssp wait
delay.
clrwdt ; avoid watchdog spoiling
your day. placed here to maximise ssp delay time
; wont be executed after flash
write but will be when ssp is read
bcf status,rp0
retlw 020 ; number of words per row, used after erase

Measuring battery voltage on 'always-on' applications where Vbat is higher than the
micro's supply can be harder than it first appears.
You need to divide down the voltage to a range suitable for the ADC or comparator,
however you don't want to waste power in the divider.
You can't use a high-impedance divider as the ADC input pin leakage will cause errors.
Switching the divider is non-trivial due to the level-shifting requirements.

Here's a neat (and very cheap) solution, which simplifies the level-shift requirements
by making use of the fact that the divider only needs turning on for long enough to make a
measurement, and so uses a capacitor to do the level-shifting.

The 'OUT' pin is any output, and can ofen be shared with other functions. When it is
driven low, the divider is turned on for long enough to make a battery voltage measurement
using the ADC (or comparator with internal reference divider).

If size is really tight, the transistor can be replaced by a digital transistor, which
incorporates the top two resistors in the package.

Incidentally, for a voltage regulator for these 'always-on' type applications, I really
like the Holtek HT71xx parts (HT7150 for 5.0v. HT7133
for 3.3v etc.). Their quiescent current is a couple of microamps, they are cheap, and have
the unusual capability to accept inputs up to 24V.

The following is an extremely compact routine for sending asynchronous serial data at
9600 baud (with 4mhz osc). It uses only 16 instructions
and 2 registers. Its compactness makes it useful for temporarily adding to programs for
sending debug data etc. You can increase the
number of stop bits up to 6 if the receiver needs gaps between bytes.

The speed isn't exactly 9600, but is only 0.16% fast. The output polarity is inverted, for
direct feeding into an RS232 port. For true
data, swap the bsf and bcf instructions. Note the use of GOTO $+1 as a 2-cycle NOP - this
is also very useful for writing compact I2C code.

For many years I've been irritated by the relatively large amount of code needed to talk
to I2C EEPROMS, especially on c5x parts where stack depth is limited. I swore one day I'd
try to do some highly optimised code. I finally got around to it, and 30-odd sheets of
paper later, below is the result

It's primarily targetted at c5x applications, and was designed with the following criteria
& limitations in mind (based on several real applications) :

Must be able to read or write one *or more* bytes to a single small (<=256 byte)

I2C eeprom, transferring data to/from an area of RAM pointed to by the FSR.

I2C address of eeprom fixed at assemble time.

Automatic retry if the eeprom is busy with a previous write.

Correct I2C timing at 4MHZ.

SDA and SCL will be on the same port, but no restrictions on which bits are used.

No assumptions to be made on the state of SCL and/or SDA on entry - this is important
where pins are
shared with other funcions - SCL can often be re-used as the eeprom will ignore it as long
as SDA is stable.

No jump table schemes (as used in Microchip's code in the 12CE518 data sheet) to allow
the code body to be
moved out of page zero if required

The following limitation of the current code is considered acceptable for the applications
targeted :
The number of bytes to read/write, and the RAM address to read/write are likely to be
fixed at assemble time, so it doesn't matter if strange offsets etc. are required to these
values, as this costs no code space, and can be handled easily with macros.

The code will read or write up to 14 bytes per call, although the write cache size of
small eeproms will usually limit writes to 8 bytes or
fewer. READ THE DATASHEET CAREFULLY to find the cache size for the device you are using,
and be aware that this can vary between different manufacturers of the same part, and part
revisions (e.g. Microchip 24LC01 vs. 24LC01B).

The current code body uses 68 words (including return). The code used to set-up addresses
etc. is not counted as this will be different in different applications. Three words can
be saved if 'fast mode' eeproms are used (e.g. 24LC01B at 5V). 4 registers are used,(plus
FSR), one of which is for the eeprom address, which is preserved and can be eliminated if
only one address will be used. No internal CALLs are used.

Explanatory notes - the following notes describe the more subtle aspects of the
code - you don't need to understand them if you just want to use the code 'as is', but you
will if you want to modify or optimise it further!

The code runs in two 'phases' - a write phase and a read phase, the latter only being used
for read operations.
Each phase begins with a start condition, followed by the control byte.
The write phase sends the control byte, the eeprom address, and for write operations, the
data to be written.
The read phase sends the control byte and reads the bytes from the eeprom.

The variable 'cnt' holds two counts, one per nibble, stored as negative numbers, i.e.
counting up to zero.
Bits 7-4 hold the number of bytes in the write phase
Bits 3-0 hold the number of bytes in the read phase
The flags byte is used as follows :
bit 0 'read' is set for reading bytes, cleared for writing
bit 1 'addr' is set after the eeprom address has been sent, to ensure it only gets sent
once.
bit 2 'rden' is a 'read pending' flag, which causes a switch to read mode after the second
control byte has been written
bits 7..5 are used as a bit counter for the byte send/recive section.
Using the same byte for flags and the bit count doesn't actually take any more code - the
extra cycles to increment the count
it by adding 20h are saved by not having to initialise the count - it's done when the
flags are set up.

When SCL is set high, if SDA is tri-state (input or '1' output), the SDA output register
bit may get set high
(which would prevent SDA going low) by read-modify-write bit operations on SCL. This
problem is avoided by clearing
both SDA and SCL bits together with ANDWF. This will not cause SDA glitches, as the only
time this clearing will
change the SDA output register state is when SDA is tri-stated.

FSR is incremented on every byte - there's no point doing it conditionally as all that's
needed to compensate
is an assembly-time offset.

Note that in a few cases you will need to add a clrwdt somewhere inside the retry loop,
depending on choice of
eeprom, WDT prescale setting, and the delay between a write and any subsequent read or
write attempt.
The worst-case write time of a 24LC01B is 10mS, and the PIC's worst-case undivided
watchdog period is 9mS.

Everyone's application is different, and so there is scope for further optimisation
depending on
particular requirements - here are a few suggestions :
If only one eeprom address is needed (e.g. a single parameter block), the eeadr register
can be replaced
with a literal.
If only single byte reads & writes are required, a couple of optimisations are
possible - the INCF FSR goes, and the
conditional write to INDF can be an unconditional write to the target register, as it
doesn't matter if it gets
written with rubbish before the actual data is read.
If the routine is called from several places, some of the set-up done in the macros could
be placed
at the head of the code, depending on what parameters are the same for all calls.
If the system timing is such that the eeprom will never be busy writing when an attempt to
access it again is made,
the 2 words of retry code can be omitted.
It may be possible to simplify some of the start/stop condition code if it is known that
other I/O activity
will not affect the state of the SCL/SDA porta and TRIS registers between calls.
The retry on busy could easily be changed to return immediately, for example by replacing
the
'goto retry_iic' with 'retlw 1'. The calling code could then check W to test for success.
Some of the delays (as noted in comments) can be omitted for 'fast mode' compatible
eeproms.

If power consumption is an issue, you may want to add a delay to the retry loop to reduce
the number of retries that will be attempted when the eeprom is busy writing.

A 10K pullup resistor is required from SDA to Vdd. No pullup is required on SCL as
open-drain SCL drive is not required for eeprom applications.

Note that this code will not work as-is with the 12CE51x devices, and although it could be
modified, it may not be especially optimal - the CE
devices don't (all?) have a write cache, so the multi-byte write capability will not be
useful in its current form.