Having recently received my Raspberry Pi, one of the first things I wanted to do was hook up a real-time clock chip I had lying around (a NXP PCF8563) and learn how to drive I2C from the BCM2835 hardware registers. Turns out it’s quite easy to do, and I think makes a useful project to learn with.
After getting it to work with the kernel drivers, I created some C code to drive the RTC chip directly using the BCM2835 I2C registers, using mmap() to expose Peripheral IO space in the user’s (virtual) memory map, the technique I learned from Gert’s Gertboard demo software, though my code’s simpler (hopefully without limiting functionality!).
Note: Revision 2 boards require the code to access BSC1 (I2C1) rather than BSC0 (I2C0), so changes to the peripheral base address may be required, or in the case if the Linux I2C driver, a reference to i2c-1 rather than i2c-0. It should be simple enough, but I don’t want to write about things I haven’t done or tested, so a bit of extra work by the reader may be required.
The PCF8563 datasheet’s reference schematic is all I used for the Raspi’s clock, though for the crystal I ditched the the trimmer capacitor from the datasheet’s schematic and used two 10pF caps as per datasheet – good enough for the stability that I’m likely to need.
This translates to the following circuit on a breadboard. The large double-layer capacitor can be found easily at Maplin or Farnell, a 1F 5.5V, and while physically quite a bit larger than a CR2032 (or similar) battery, it should give months of power between charges. An electrolytic capacitor would also work for testing, but use a 150-300 ohm resistor in series to limit the current draw when charging, or your Raspi will likely reboot (since we’re only allowed to draw 50mA from the 3V3 pin).
The four wires hooked up to the Raspberry Pi’s expansion header are simply 3V3, GND, I2C0_SCL and I2C0_SDA (pins P1-01, P1-06, P1-05 and P1-03 respectively based on the Wiki reference).
Take care when wiring up the expansion header, since it’s easy to make mistakes when translating pins from diagram to board. It’s always worth at least checking voltage as reference points (e.g. measure +3.3V across what you think are pins 3V3 and GND) to make sure you’re not top to bottom on your translation, for example, before plugging up.
The circuit is so simple that it could just be wired together onto a 26-pin header connector, given a little care and forethought, and perhaps a bit of epoxy putty or similar to fix it all together.
The Kernel Driver
Next I had to think about software – it turned out that between Chris Boot and Frank Buss, there was already a kernel with support for the Raspi’s I2C bus in a way that the existing pcf8563 driver can use.
When running Chris’s kernel, the above commands are all that are required to get the PCF8563 working with the hwclock command. The value 0×51 above is the hardwired I2C device address of the RTC chip.
And that’s it! Chris’s 3.2.18 kernel was screwing up my SD card access periodically (when it boots, it runs more or less flawlessly, but it often fails to boot) and ended up corrupting an otherwise good SD card image. I think this issue has been fixed since his 3.2.19 r3 build, but I haven’t tested it.
The C Code Alternative
My original intention had been to learn how to drive the Raspberry Pi’s IO Peripheral devices at a register level, so having seen that the circuit was wired and working using the kernel drivers, I went back to the Foundation’s official kernel, which has no inbuilt I2C support, to find out how to drive the chip in user-space.
After a quick attempt to access the IO peripheral space directly using pointers (ok, daft to even try, but you never know…), I had a look at the source code of Gert’s demo, which accesses GPIO among others, to see how it can be done.
Essentially, he accesses /dev/mem, which is a virtual file that makes available the physical address space (ARM Physical Addresses in the BCM2835 datasheet), via a chunk of user-space memory that’s judiciously allocated using malloc(), and that’s mapped to /dev/mem using the mmap().
I simplified this by allowing the kernel supply me with the virtual address of the physical address space, on the basis that it will automatically allocate on a page boundary. All the BCM2835 peripherals I’ve looked at in the datasheet are based at a 4K boundary.
If you’re used to driving I2C from a microcontroller, either bit-banging (driving the SCL and SDA pins directly with software) or with an MCU peripheral (through hardware registers), then you may find that the BCM2835 hardware does a little more for you than you’re used to. In particular, you use hardware registers to define the I2C device-address and the number of data-bytes (e.g. BSC0_A & BSC0_DLEN), and the BCM2835 handles the writing of the address, the bytes, and the corresponding acknowledge bits for you automatically.
For more detail: Raspberry Pi PCF8563 Real Time Clock (RTC)