I2C slave device17 January 2021
A significant issue I had with my redesigned USB-I2C master was testing and in particular the checking of error conditions, so I decided to make a test platform for this purpose. Originally I was going to make a dedicated board including DIP switches but I soon realised that test configuration could also be done via the I2C connection, so instead I built the hardware as a break-out board since the
PIC16F1823with RS232 and I2C is a common combination I use as a base for projects.
Circuit designThe circuit itself is the I2C master with a 14-pin
PIC16F1823rather than an 8-pin
PIC12(L)F1840, and the circuit is powered via an on-board voltage regulator supplied via a barrel jack rather than the USB bus. A flash header is also included because it is by far the most convenient way to reprogram a microcontroller, and all the unused I/O pins have been fanned out. Note that between the
FT230XSthe RS232 pins are connected Tx-Rx & Rx-Tx and not Tx-Tx & Rx-Rx — getting this wrong is why the PCB went to a second revision.
An ulterior motive with this project was creating a KiCad footprint for the
PIC16F1823 and this in turn is based on the foorprint for the
PIC12F1822 from the same chip series.
Several of my recent projects would have used
PIC16F1823 if it was not for the absence of the footprint in the standard library, so it was time I got round to making my own.
This footprint is included alongside the PCB itself within my Bitbucket repository.
List of componentsI found it hard to see the cathode markings on the Osram
LM R976SMD LEDs and as a result had soldered some of them the wrong way round, and the CUI Devices
PJ-102AHbarrel jacks are also tricky to source, but they are components I had at hand so decided to use them up.
|U1||5v power regulator||Texas Instruments||
|R1||1kΩ protective resistor||Multicomp||
|R2,R3||27Ω USB resistors||
|C1,C2||0.1μF bypass capacitor||Samsung||
|J1||Barrel Jack||CUI Devices||
FirmwareWhereas the I2C routines that were used within my I2C master have long since been rewritten in assembly and used in many subsequent projects, the same is not the case with the I2C slave routines I have written as I have preferred to use RS232 instead. Therefore the firmware here is a reimplementation in assembly of the routines in the register-enabled slave from late-2018, which I decided to do from scratch. The procedures are summerised in the flow-chart below. Storing of incoming data can be done on either branch of the Acknowledge Time node but I found it more convenient to do it before dispatching the acknowledgment.
Start and stop bitsI did not quite figure out start bit interrupts but I also saw no real need for them either. However getting interrupts for stop bits is useful because it helps in distinguishing between start and restart bits, which in turn can be used to allow a device to support a variable number of register addresses in read transactions. They can also be used as an end-of-transaction indicator rather than assuming fixed transaction lengths.
USB side-channelI have found it very useful to have a serial side-channel as a way of getting debugging information to the outside world, which is particularly critical in cases like this where using simulation is not an option. When I wrote the first version of the slave firmware I was having to deduce internal state rather than being able to probe it directly, which makes things very difficult. Having used the side-channel for development I left in tidied-up versions of the debugging messages, and an example transaction log is shown below:
A:20 D:0a D:0b A:21 R:a R:a P
A is an incoming address byte value,
D an incoming data byte,
R is the ack/nack status of an outgoing byte, and
P is a stop bit.
Such use of RS232 in this way is also a good demonstration of the usefulness of clock stretching in I2C — without it I doubt that the UART transactions to print out the information would complete in time to avoid a buffer collision.
Address holdNormally the microcontroller hardware automatically acknowledges incoming bytes, but by using address hold this decision on whether a matching I2C address should be acknowledged is instead passed on to the firmware. When I first tried implementing address holding back in late-2018 it seemed to be broken and looking back the symptoms of the fault it could plausibly have been an issue with whatever chip I was using. Having said that I did not consider the feature useful at the time so my investigation into it was limited. I still don't really see much use for it beyond testing some of the failure paths in the complimentary I2C master firmware, and for those tests I simply hard-coded the negative-acknowledge for the test-cases.
Data holdLike address hold, data hold lets the firmware decide whether a data byte should be acknowledged or not. Although unlike address hold it seemed to work properly in the previous firmware in hindsight I am not entirely sure whether how things were done was entirety correct, although this is a moot point as I don't write PIC firmware in C any more. For testing purposes incoming data bytes of
0xffare not acknowledged which is sufficient to test most of the failure reporting cases.
RemarksPartly due to the design of the circuit being a bit rushed I got the internal RS232 connection between the USB converter and the microcontroller the wrong way round, which by any measure was a frustrating mistake to make. As a result the firmware development and testing of my I2C master was instead done using an alternative board equipped with a
PIC16F1454back in November, which thankfully needed very few changes to make it work with the
PIC16F1823once the revised PCB for the latter arrived. I did not have a 5-volt regulator to hand so I omitted the internal power supply and instead powered the slave via the ICSP header.
I tested the slave device with an off-the-shelf I2C adapter and then used this slave to test my own I2C master — in the process I found several faults with the latter's firmware which have now been fixed. While I have not noticed problems with my previous C-based I2C master and I2C slave routines I have more confidence in the correctness of my new assembly-based ones. The one thing the firmware does not guard against is spurious behaviour when pull-up resistors in place, but I think this is not possible to do and would explain why off-the-shelf adapters have built-in ones that cannot be disabled. I suspect that at some point I will reimplement my LED Matrix Tile firmware in pure assembly as it is the last bit of firmware written in C that I expect to have future interest in.