I revisited Windows IoT Core a few weekends ago and found it to be useful in a quick project that involved dumping my wireless headset’s electrically erasable programmable read-only memory (EEPROM). In other words, just another weekend at Rafael’s place.
But first, a little background.
I own a reasonably old wireless headset–Turtle Beach Ear Force PX3–that I’ve been keeping on life support because of its premium recording capabilities. Thus far, I’ve found no other headset that can record audio as well as the PX3. (Well, there was the Plantronics Audio 995 but I’ve gone through all the reserves left on this planet.) So I’ve invested in reverse engineering my headset to ensure I can restore function to pieces that need replacement. For example, I’ve rebuilt its poorly designed plastic buttons in CAD software for 3D printing on-demand.
The headset has a nugget of memory (EEPROM) that stores configuration information for the programmable audio equalization features of the headset. And while I don’t use these features, the headset fails to boot up if that data is corrupted or missing. And with EEPROM having a finite amount of reads before going kaput, it’s a ticking time-bomb. I need to back up its contents.
So let me walk you through what I did.
First, I pulled down the Windows IoT Core Dashboard and set up a new device. I flashed a MicroSD card with the latest build of Windows IoT Core and popped it straight into the Raspberry Pi 2 (Amazon, ~$40). After a bit of configuration, the device was online and ready to receive instructions from Visual Studio.
I then skimmed through the datasheet and attached some test clips to the EEPROM chip (ACE24C256) for Ground, Serial Clock and Serial Data access. I routed those to the Raspberry Pi’s GPIO2, GPIO3 and Ground pins where Two-Wire Serial (TWI)/Inter-integrated Circuit (I2C) communication is expected to occur.
That was the extent of the hardware configuration I performed.
(Two-Wire Interface is mostly identical to I2C, cleverly created to sidestep trademark issues with I2C. I say mostly because it’s missing a few niceties like 10-bit addressing.)
Windows IoT Core runs apps targeting the Universal Windows Platform (UWP), but the templates that ship with Visual Studio are way overkill for what I needed. So, I instead installed the Background Application Template from the Visual Studio Marketplace. This template creates a simpler project with a bare-bones implementation of a Background Task.
Using that starter code, I followed the usual deferral pattern (to prevent the app from killing my asynchronous tasks) and started laying down the code I needed:
- Retrieved an instance of the I2cController class, corresponding to the default I2C controller (and set of pins) on the Raspberry Pi.
- Retrieved an instance of the I2cDevice class, representing the chip I wanted to dump. This requires an I2cConnectionSettings object that points to the chip via a 7-bit numerical address. In this chip’s case, the address is crafted by setting its first 4 significant bits to  and the next 3 to . That becomes  or 50 in hexadecimal. (The I2C classes manage the 8th bit on the fly.)
- Took advantage of the faster clock speeds supported by the chip by setting the device Bus Speed to FastMode (400kHz).
- Prepared a file for holding the EEPROM contents
I paused at calling the I2cDevice.Read method for a moment, because it only took one parameter – a buffer to store data clocked out of the chip. It doesn’t support addressing. But I remembered I2C/TWI isn’t limited to just storage applications – it’s higher level than that. So I needed to review the datasheet again to understand the protocol implemented on top of I2C/TWI to read data correctly from the chip.
Here’s what I picked up from the datasheet:
- A Current Address Read is performed by clocking in a read bit (0). The chip responds by clocking out the byte of data at the current address.
- A Sequential Read is performed by looping a Current Address Read, paired with an acknowledgement, to effectively move the address pointer forward by 1 byte each iteration.
- A Random Read is performed by first preparing the chip for a dummy write operation, to set its internal address pointer, then switching mid-way to a Current Address Read operation. Sneaky!
I implemented this logic using the I2CDevice.Read and I2CDevice.Write methods on the Device object I created earlier. Because the I2cDevice class handles I2C/TWI addressing and other minutia (in green) for me, I only had to worry about the data portion of the protocol (in purple). In this case, to set the internal address pointer to 0, I had to write two zeros (8 bits each). Got it.
With the dummy write out of the way, I switched to using the I2CDevice.Read method to complete the Random Read operation. The I2C classes did the heavy lifting while I dumped my buffer to disk. I then come behind the app to scoop up the file with the IoT Dashboard.
With a fresh EEPROM dump in hand, I was done.