Introduction to External Memory with PIC Microcontrollers

PIC microcontrollers from Microchip offer internal EEPROM and Flash for storing application code and small amounts of data, but complex projects often require more capacity. External EEPROM (Electrically Erasable Programmable Read-Only Memory) and Flash memory chips provide the extra non-volatile storage needed for data logging, configuration parameters, firmware updates, and multimedia assets. This expanded guide walks you through selecting, connecting, and programming these external memory devices with PIC microcontrollers, covering both I²C and SPI interfaces in depth.

Whether you are building a data recorder, a smart sensor node, or an embedded system that must retain critical settings after power loss, understanding how to interface external memory efficiently will save development time and improve reliability. We will discuss hardware wiring, communication protocol details, practical code examples, error handling, and advanced techniques like wear leveling and block management.

Understanding External EEPROM and Flash Memory

Both EEPROM and Flash are non-volatile memory technologies, meaning they preserve stored data when power is removed. However, they differ in architecture, endurance, and typical use cases:

  • EEPROM – Supports byte-level read and write operations. Its endurance is typically 1 million erase/write cycles per byte. EEPROM is ideal for storing small amounts of frequently updated data (e.g., calibration constants, user preferences). Common capacities range from 1 kbit to 512 kbit.
  • Flash Memory – Optimized for block-level erase and program operations. Flash offers much higher density (megabits to gigabits) but lower endurance (typically 10,000 to 100,000 erase cycles per block). It is used for firmware storage, large data logs, and file systems. NOR Flash allows random access reads; NAND Flash requires page-level access and is more suited for bulk storage.

External EEPROM and Flash communicate with the PIC via serial protocols (I²C or SPI) or rarely parallel buses. Serial interfaces reduce pin count and simplify PCB layout, making them the dominant choice for embedded designs.

Choosing the Right Memory Device

When selecting an external memory chip for your PIC project, evaluate these criteria:

  • Memory Size and Capacity – Estimate the data volume your application generates or stores. For simple configuration data, a 64 kbit EEPROM (8 KB) may suffice. For audio samples or firmware upgrades, consider 16 Mbit or larger SPI Flash.
  • Communication Protocol – I²C uses only two wires (SDA and SCL) and supports multiple devices addressing; it is slower but simpler. SPI uses four wires (MISO, MOSI, SCLK, CS) and achieves higher speeds but requires a dedicated chip select per device. Choose based on your speed and pin availability.
  • Speed and Data Transfer Rate – I²C typically runs at 100 kHz or 400 kHz; SPI can reach tens of MHz. If you need fast streaming (e.g., audio playback), SPI is mandatory. For occasional reads of small config blocks, I²C is fine.
  • Power Consumption – Both EEPROM and Flash have active read/write currents in the milliampere range and deep sleep modes. For battery-powered designs, select chips with low standby current (e.g., 1 µA or less).
  • Operating Voltage – Most PICs work at 3.3V or 5V. Ensure the memory chip supports the same voltage range. Many modern EEPROMs operate from 1.7V to 5.5V, providing flexibility.
  • Cost and Availability – Common families include Microchip’s 24AA series (I²C EEPROM), 25AA series (SPI EEPROM), SST25 or Winbond W25Q series (SPI Flash), and AT24 series (I²C EEPROM). Check distributor stock and long lead times.
  • Endurance and Retention – EEPROM usually offers higher write endurance than Flash. Data retention is typically 40+ years. For Flash, consider the number of erase cycles and implement wear leveling if writing frequently.

Microchip’s serial EEPROM selection guide provides direct comparison tables. Winbond’s serial NOR Flash portfolio lists popular SPI Flash options. Always read the specific datasheet for timing and command sets.

Connecting External Memory to the PIC Microcontroller

Hardware wiring depends on the interface. Below we detail I²C and SPI connections along with important considerations like pull-up resistors and decoupling capacitors.

I²C (IIC) Interface

I²C uses two bidirectional lines: SDA (serial data) and SCL (serial clock). The master (PIC) controls the clock and initiates transactions. Each slave has a 7‑bit or 10‑bit address. For EEPROM, the address often comprises a fixed prefix plus three hardware address pins (A0, A1, A2) that allow up to eight devices on the same bus.

Wiring:

  • Connect SDA to a PIC I/O pin configured as open-drain or bi‑directional.
  • Connect SCL to another PIC I/O pin.
  • Add external pull-up resistors (typically 4.7 kΩ) from SDA and SCL to Vcc. The value depends on bus capacitance and speed; for 400 kHz, 2.2 kΩ may be needed.
  • Connect the memory’s WP (write protect) pin to GND to disable hardware write protection, or to Vcc to protect the entire array.
  • Place a 0.1 µF ceramic capacitor near the memory’s power pin to decouple noise.

For high‑speed I²C (1 MHz), keep traces short and use stronger pull-ups. Example chips: Microchip 24LC256 (256 kbit I²C EEPROM), AT24C512 (512 kbit).

SPI Interface

SPI uses four signals: MOSI (master out, slave in), MISO (master in, slave out), SCLK (serial clock), and CS (chip select, active low). Each SPI slave requires a dedicated CS line from the PIC. Multiple slaves can share MISO, MOSI, SCLK if they are not simultaneously selected.

Wiring:

  • Connect MOSI (PIC output) to memory’s SI (serial input).
  • Connect MISO (PIC input) to memory’s SO (serial output).
  • Connect SCLK (PIC output) to memory’s SCK.
  • Connect CS to a PIC digital output pin. For multiple memories, use separate pins.
  • Add weak pull-up (10 kΩ) on the CS line if needed, though typical logic suffices.
  • Decouple power with a 0.1 µF capacitor.

SPI Flash chips often have status registers and need specific command sequences (e.g., WREN before write). Example chips: Winbond W25Q32 (32 Mbit SPI Flash), Microchip 25LC256 (256 kbit SPI EEPROM).

Programming the PIC to Access External Memory

The PIC microcontroller must initialize the appropriate peripheral module (I²C or SPI) and then send/receive data according to the memory chip’s protocol. Both Microchip’s hardware MSSP module (Master Synchronous Serial Port) and software bit‑banging can be used. The MSSP is preferred for reliability and speed.

I²C Communication Code Example

Below is a simplified but functional example using the MCC (Microchip Code Configurator) generated HAL for a PIC16F18877. The code writes a single byte to an I²C EEPROM at a given address and then reads it back.

// Assumes I2C1_Initialize() called earlier.
// Device address: 0xA0 (for write) – actual address depends on A0-A2 pins.
#define EEPROM_ADDR 0xA0

void I2C_WriteByte(uint16_t memAddr, uint8_t data) {
    I2C1_Start();                 // Generate start condition
    I2C1_Write(EEPROM_ADDR);      // Send device address + write bit
    I2C1_Write((uint8_t)(memAddr >> 8));  // High address byte
    I2C1_Write((uint8_t)(memAddr));       // Low address byte
    I2C1_Write(data);             // Data byte
    I2C1_Stop();                  // Generate stop condition
    __delay_ms(5);                // Wait for internal write cycle (tWR)
}

uint8_t I2C_ReadByte(uint16_t memAddr) {
    uint8_t data;
    I2C1_Start();
    I2C1_Write(EEPROM_ADDR);      // Device address + write for dummy write
    I2C1_Write((uint8_t)(memAddr >> 8));
    I2C1_Write((uint8_t)(memAddr));
    I2C1_RepeatedStart();         // Repeated start to change direction
    I2C1_Write(EEPROM_ADDR | 0x01); // Device address + read bit
    data = I2C1_Read(0);          // NAK after last byte
    I2C1_Stop();
    return data;
}

Note: For page writes, you must not cross a page boundary (typically 8, 16, or 64 bytes). Consult the datasheet for page size. Also, polling for acknowledgment after the write cycle is more efficient than fixed delays.

SPI Communication Code Example

For SPI Flash (e.g., W25Q32), write operations require enabling write (WREN), then sending a Page Program command. Here is a minimal example using MSSP in SPI master mode.

#define FLASH_CS  LATAbits.LATA0   // Chip select pin

void SPI_WriteEnable(void) {
    FLASH_CS = 0;
    SPI1_Exchange8bit(0x06);  // WREN command
    FLASH_CS = 1;
}

void SPI_FlashPageProgram(uint32_t addr, uint8_t *data, uint8_t len) {
    SPI_WriteEnable();
    FLASH_CS = 0;
    SPI1_Exchange8bit(0x02);                // Page Program command
    SPI1_Exchange8bit((uint8_t)(addr >> 16)); // High address byte
    SPI1_Exchange8bit((uint8_t)(addr >> 8));
    SPI1_Exchange8bit((uint8_t)(addr));
    for (uint8_t i = 0; i < len; i++) {
        SPI1_Exchange8bit(data[i]);
    }
    FLASH_CS = 1;
    __delay_ms(5);  // tPP max 5 ms typical
}

void SPI_FlashRead(uint32_t addr, uint8_t *buffer, uint32_t count) {
    FLASH_CS = 0;
    SPI1_Exchange8bit(0x03);                // Read command
    SPI1_Exchange8bit((uint8_t)(addr >> 16));
    SPI1_Exchange8bit((uint8_t)(addr >> 8));
    SPI1_Exchange8bit((uint8_t)(addr));
    for (uint32_t i = 0; i < count; i++) {
        buffer[i] = SPI1_Exchange8bit(0x00);
    }
    FLASH_CS = 1;
}

Flash memory cannot be written without erasing an entire sector (typically 4 KB). Always erase the sector before reprogramming it. Use the Sector Erase command (0x20) followed by polling the status register until the write-in-progress bit clears.

Advanced Techniques: Wear Leveling, Page Management, and Firmware Updates

When storing large amounts of data or performing frequent writes, consider these strategies to extend memory life and maintain performance.

  • Wear Leveling for EEPROM – Distribute writes across multiple locations. For example, maintain a circular buffer index in a dedicated EEPROM slot and write data to the next free page. This prevents early aging of a single byte.
  • Flash Block Management – Use a simple file system or metadata table to track which sectors are erased/in use. Typical consumer SPI Flash chips have 4 KB sectors; erasing a sector takes tens of milliseconds.
  • Firmware Over-the-Air (FOTA) Updates – External Flash can hold a new firmware image. The PIC bootloader reads the image, writes it to internal Flash, and resets. Ensure power loss during write is handled via a double‑buffer or checksum verification.
  • Error Detection and Correction – For critical data, append CRC or simple XOR checksums. On read, verify integrity. Many external memories do not have built‑in ECC, so software checks are prudent.

Best Practices and Troubleshooting

Follow these guidelines to minimize issues and achieve robust operation.

  • Always Consult the Datasheet – Timing parameters, command sets, and pull‑up specifications vary between manufacturers. Never assume compatibility.
  • Use Proper Pull-Up Resistors for I²C – The value depends on bus capacitance and clock speed. For a bus with only one or two devices and 400 kHz, start with 4.7 kΩ. Use an oscilloscope to verify rising edges.
  • Implement Acknowledge Polling – Instead of fixed delays after writes, repeatedly send the device address until an ACK is received. This adapts to variable write times.
  • Keep SPI Traces Short – SPI runs at MHz frequencies; long wires cause ringing. Add series resistors (22–47 Ω) near the PIC output to dampen reflections.
  • Use Decoupling Capacitors – Place 0.1 µF ceramic as close as possible to each memory chip’s Vdd pin.
  • Test with Known Patterns – Write alternating 0x55, 0xAA, and verify. Check address boundaries (e.g., writing across a page boundary should be avoided).
  • Monitor for Bus Contention – If multiple SPI devices share MISO, ensure only one chip select is active at a time. For I²C, ensure no address conflicts.
  • Handle Write Protection – Many memories have a WP pin. Either pull it low to enable writing or control via GPIO if you need selective protection.

Example Project: Data Logger with SPI Flash

To solidify the concepts, consider a simple data logger that periodically reads an analog sensor, stores the value in SPI Flash (e.g., W25Q16), and uses a circular buffer to overwrite oldest data. The PIC wakes from sleep, performs a conversion, writes to the current sector block, updates a pointer in EEPROM, and returns to sleep. The external Flash retains years of data with minimal power consumption.

Key steps:

  1. Initialize SPI MSSP, configure CS pin as output.
  2. On startup, read the current write pointer from a small I²C EEPROM (or from the first sector of Flash).
  3. Each logging cycle: read ADC → write to Flash page → increment pointer. If pointer reaches end of allocated area, reset to start (circular).
  4. Optionally compute CRC of the last page to detect corruption.
  5. Use low‑power sleep between intervals.

This pattern can be extended to multiple sensors, timestamping, and event‑driven logging.

Conclusion and Further Resources

Integrating external EEPROM and Flash memory with PIC microcontrollers opens a wide range of applications that require more storage than internal memory can provide. By selecting the appropriate interface (I²C or SPI), wiring correctly, and implementing robust read/write routines, you can add reliable non‑volatile storage to any embedded system. Advanced techniques like wear leveling, page management, and Acknowledge polling ensure long‑term durability and predictable performance.

For further reading, explore the following resources:

With careful design and thorough testing, external memory can seamlessly scale your PIC projects to handle larger data sets, firmware updates, and persistent configuration needs. Start with a simple write/read loop, verify with a logic analyzer or oscilloscope, and then build complexity incrementally.