Pi Pico EEPROM Programmer

by TomGoff in Circuits > Microcontrollers

185 Views, 0 Favorites, 0 Comments

Pi Pico EEPROM Programmer

programmerPic.jpg
Pi Pico EEPROM Programmer Video 1 - Using Thonny and Micropython to program the Pico

As with many of the projects I publish, this is not an entry level project. This is more aimed at level 3, the point at which you have advanced from making simple projects to making things a bit more interesting but still accessible to build at home with simple tools over a weekend.

This project could be used as a useful tool for your retro computer projects, for programming ROMs. However, I have a different reason for programming ROMs. I have recently been very interested tinkering with automata that do not use a CPU (central processing unit) for control. Instead of the process being controlled by a CPU, a set of instructions are stored in a memory chip, such as an EEPROM (Electronically Erasable Read Only Memory). The machine cycles trough the memory an performs an operation that is dependant on the data stored in the memory address. This machine is similar to a computer but with out the ability to branch, it's really an electronic automata (a deterministic finite automata). An early mechanical example of this is the Jacquard loom (Jacquard machine - Wikipedia).

To build my electronic automata I needed a method to program the EEPROM. Whilst this could be undertaken manually, with a series of push buttons and switches, it is very time-consuming and I wanted to build a tool to automate the programming process. There are several projects online that demonstrate how to make an EEPROM programmer with an Arduino. If you use the entry level Arduino dev boards (such as the Uno and Nano) you require a shift register to increase the number of Inputs and Outputs (I/O), required to program a parallel EEPROM. I wanted to design an EEPROM programmer that didn't require an external shift register, this is where the Raspberry Pi Pico came in. The raspberry Pi Pico has many more pins available to be configured as either Inputs or Outputs making it the ideal choice for programming the EEPROM.

I will be posting my EEPROM Electronic Automata as a future project.

Supplies

IMG_0748.jpg

Here's a list of the tools and consumables I used for the Pi Pico EEPROM programmer:


Materials:

Raspberry Pi Pico

2 x 20-pin Header Pins for Pi Pico 2.54mm Pitch

24-pin EEPROM AT28C16

ZIF (Zero Insertion Force) Socket 28 Pin 2.54mm Pitch

single-sided prototype board (perfboard) 100mm x 150mm

26 Gauge Solid Copper Hook-up Wire, 4 colours

Momentary Push button

LED (any colour)

Resistor 330 ohms

Lead-free Solder


Equipment:

Laptop (or PC)

Thonny Programming Environement

USB-A to USB-micro programming lead

Soldering Iron

Wire Strippers

Wire Cutters

Hardware Design

EEPROM_Programmer_Sch_Rev2.0.png

The hardware design is very straight forward, comprising of of the following connections:

  1. Connecting the 5V power from the Pico to the AT28C16 EEPROM VCC.
  2. Connecting the Ground from the Pico to the AT28C16 EEPROM VCC.
  3. Connecting the Output Enable pin of AT28C16 EEPROM to 5V
  4. Connecting the Chip Enable pin of AT28C16 EEPROM to 5V to Ground
  5. Connecting the address pins A0 to A07 of AT28C16 EEPROM to the correct Pico GPIO Pins
  6. Connecting the unused address pins of AT28C16 EEPROM to ground. Note: there are plenty of spare GPIO pins on the Pico if you want to connect the other 4 address pins of the EEPROM for programming.
  7. Connecting the data pins I/O.0 to I/O.07 of AT28C16 EEPROM to the correct Pico GPIO Pins.
  8. Connecting the Write Enable pin of AT28C16 EEPROM to the GPIO pin used for writing the data to the address, I used GPIO 15.
  9. Connecting a push button for "start programming" to the required GPIO pin, I used GPIO 28. The other side of the push button goes to 3.3V out on the Pico. It's very important to use 3.3V and not 5V.
  10. Connecting the Resistor and "finished programming" LED to the required GPIO pin, I used GPIO 27. The cathode (short pin, negative) or the LED goes to ground.

I have drawn a schematic of the hardware design so that you can see all of the connections.


Simulation

eeprom sim.png
Pi Pico EEPROM Programmer Video 2 - Simulation of hardware and software using Wokwi

Before I started building my project in the real world, I simulated the micropython script and electronics using the fantastic Wokwi simulator.


I have attached a video showing the simulation.


Please checkout this link to see my project:


https://wokwi.com/projects/477063587929733121

Software

PicoEEPROMCode.png

Like many tinkerers and hackers, I have become increasingly reliant in using AI assistants for writing software. This whole project has an anachronistic (out-dated) feel to it so I wanted to write the code without the use of AI. It's probably a bit clunky, but here's a breakdown of how the code works.


Lines 1–32 – Getting things set up

import machine
import time

# pin to connect to write enable
writePin = machine.Pin(15, machine.Pin.OUT)
writePin.value(1)

# pin to connect show programming complete
completePin = machine.Pin(27, machine.Pin.OUT)
completePin.value(0)

# pin to button to start programming
push_button = machine.Pin(28, machine.Pin.IN, machine.Pin.PULL_DOWN)


# Address pins to use
address_numbers = [0, 1, 2, 3, 4, 5, 6, 7]

# data pins to use
data_numbers = [16, 17, 18, 19, 20, 21, 22, 26]

# set address pins as outputs
address_pins = []
for address_num in address_numbers:
a_pin = machine.Pin (address_num, machine.Pin.OUT)
address_pins.append(a_pin)

# set data pins as outputs
data_pins = []
for data_num in data_numbers:
d_pin = machine.Pin (data_num, machine.Pin.OUT)
data_pins.append(d_pin)


This section imports the required MicroPython libraries and configures the Raspberry Pi Pico GPIO pins. Individual pins are assigned for the EEPROM write-enable signal, programming-complete indicator and start push button. Two lists then define the eight address pins and eight data pins, which are configured as outputs.

Lines 34–68 – Data for the EEPROM

# data to be loaded into eeprom
patterns = [
0x77,0x65,0x77,0x69,0x73,0x68,0x79,0x6F,
0x75,0x61,0x6D,0x65,0x72,0x72,0x79,0x63,
0x68,0x72,0x69,0x73,0x74,0x6D,0x61,0x73,
0x61,0x6E,0x64,0x61,0x68,0x61,0x70,0x70,
0x79,0x6E,0x65,0x77,0x79,0x65,0x61,0x72,
0x77,0x65,0x77,0x69,0x73,0x68,0x79,0x6F,
0x75,0x61,0x6D,0x65,0x72,0x72,0x79,0x63,
0x68,0x72,0x69,0x73,0x74,0x6D,0x61,0x73,
0x61,0x6E,0x64,0x61,0x68,0x61,0x70,0x70,
0x79,0x6E,0x65,0x77,0x79,0x65,0x61,0x72,
0x77,0x65,0x77,0x69,0x73,0x68,0x79,0x6F,
0x75,0x61,0x6D,0x65,0x72,0x72,0x79,0x63,
0x68,0x72,0x69,0x73,0x74,0x6D,0x61,0x73,
0x61,0x6E,0x64,0x61,0x68,0x61,0x70,0x70,
0x79,0x6E,0x65,0x77,0x79,0x65,0x61,0x72,
0x77,0x65,0x77,0x69,0x73,0x68,0x79,0x6F,
0x75,0x61,0x6D,0x65,0x72,0x72,0x79,0x63,
0x68,0x72,0x69,0x73,0x74,0x6D,0x61,0x73,
0x61,0x6E,0x64,0x61,0x68,0x61,0x70,0x70,
0x79,0x6E,0x65,0x77,0x79,0x65,0x61,0x72,
0x77,0x65,0x77,0x69,0x73,0x68,0x79,0x6F,
0x75,0x61,0x6D,0x65,0x72,0x72,0x79,0x63,
0x68,0x72,0x69,0x73,0x74,0x6D,0x61,0x73,
0x61,0x6E,0x64,0x61,0x68,0x61,0x70,0x70,
0x79,0x6E,0x65,0x77,0x79,0x65,0x61,0x72,
0x77,0x65,0x77,0x69,0x73,0x68,0x79,0x6F,
0x75,0x61,0x6D,0x65,0x72,0x72,0x79,0x63,
0x68,0x72,0x69,0x73,0x74,0x6D,0x61,0x73,
0x61,0x6E,0x64,0x61,0x68,0x61,0x70,0x70,
0x79,0x6E,0x65,0x77,0x79,0x65,0x61,0x72,
0x77,0x65,0x77,0x69,0x73,0x68,0x79,0x6F,
0x75,0x61,0x6D,0x65,0x72,0x72,0x79,0x63
]

This section contains the data that will be programmed into the EEPROM. Each hexadecimal number represents one byte of data, consisting of eight bits. The bytes are stored in a Python list called patterns. The position of each byte within the list determines the EEPROM memory address where it will be stored. If you want to put a secret message on your EEPROM you can use a ASCII to Hex converter using this website: String to Hex | ASCII to Hex Code Converter


Lines 70–99 – Main programming loop

while True:
#loop to sequence through addresses:
if push_button.value() == 1:
completePin.value(0)
for i in range (len(patterns)):
for address_index in range(8):
# Extract bit state (0 or 1) using right-shift and bitwise AND
address_state = (i >> address_index) & 1
# Set corresponding GPIO pin from our led_pins list
address_pins[address_index].value(address_state)
print ("address Position", address_index, address_state) # comment out to speed up programming eeprom
time.sleep_ms(10)
for bit_index in range(8):
# Extract bit state (0 or 1) using right-shift and bitwise AND
bit_state = (patterns[i] >> bit_index) & 1
# Set corresponding GPIO pin from our led_pins list
data_pins[bit_index].value(bit_state)
print ("data position", bit_index, bit_state) # comment out to speed up programming eeprom
time.sleep_ms(10)

time.sleep_ms(50) # comment out to speed up programming eeprom
print ("load data",i) # comment out to speed up programming eeprom
time.sleep_ms(50) # comment out to speed up programming eeprom
writePin.value(0) # toggle write pin low
time.sleep_ms(1) # wait 1mS
writePin.value(1) # toggle write pin high (data written to selected eeprom address)

if i == 255:
print ("Programming Complete!")
completePin.value(1)

This section waits for the push button to be pressed before programming begins. The program works through the patterns list one byte at a time. It sets the eight address pins, places the required byte onto the eight data pins, then pulses the write-enable pin to store the data in the EEPROM.

Nothing happens until the push button is pressed and then the main for loop is entered.

This is the most interesting part of the program, there is one master for loop and two other for loops nested into the main for loop. The main for loop is the length of the list containing the data to be input into your EEPROM, 256 (0-255) in my case.

The first nested for loop "for address_index in range(8):" sets the eight GPIO pins that are connected to the EEPROM address inputs. The loop runs eight times, once for each bit of the 8-bit address. The variable address_index therefore takes the values 0 through 7. The address being written to is provided by i from the main for loop. For example, if the program is writing to EEPROM address 45, the value of i will be 45. In 8-bit binary this is:45 = 0010 1101. The following line is used to examine each individual bit of this address: "address_state = (i >> address_index) & 1" The >> operator shifts the binary value to the right. The number of places it is shifted is determined by address_index. For example, on the fourth pass through the loop, address_index is 3. The value 45 is therefore shifted three places to the right: 0010 1101 >> 3 = 0000 0101

We are only interested in the bit at the far right. To isolate this bit, the program performs a bitwise AND operation with 1:


0000 0101

0000 0001

---------

0000 0001


The result is therefore 1, which is stored in address_state. The next line: "address_pins[address_index].value(address_state)" uses address_index to select the corresponding GPIO pin from the address_pins[] list. On the fourth pass, address_index is 3, so address_pins[3] is selected. This is the fourth item in the list and corresponds to GPIO pin 3. The .value(address_state) part then sets this GPIO pin HIGH or LOW. If address_state is 1, the pin is set HIGH. If address_state is 0, the pin is set LOW. The loop repeats this process for all eight bits of the address. Once all eight passes have been completed, the eight GPIO address pins contain the complete binary representation of the EEPROM address. For address 45 (0010 1101), the GPIO address pins are therefore set to represent: 0010 1101. The program can then place the required data onto the EEPROM data pins before activating the EEPROM write-enable input.

The second nested loop "for bit_index in range(8):" sets the eight GPIO pins that are connected to the EEPROM data outputs. This nested loop works the same way as the previous nested loop.


After the two nested loops have put the required values on the address and data pins, the line writePin.value(0) toggles the write enable pin of the EEPROM low for 1 millisecond, this operation writes the required data to the address selected.


Once the main loop has completed, the line "completePin.value(1)" turns on an LED to indicate that the EEPROM programming process is complete.


The program is attached, to be copied and modified as you wish.

Building Your EEPROM Programmer

programmerPic2.jpg

This is the order in which I built the programmer:

Note: I find it very useful to have a print-out of the Pi Pico and EEPROM Pinout in front of me when soldering to prevent mistakes.

  1. Solder the Pico Header pins to the top of the perfboard
  2. Solder the Zif Socket to the perfboard
  3. Solder the Momentary Push Button to the perfboard
  4. Solder the LED and Resistor to the perfboard, solder the resistor to the positive (anode) of the LED.
  5. Solder the other side of the resistor to the Pi Pico GPIO pin 27 (pin 32).
  6. Solder the LED negative leg (cathode) to the to the Ground Pin on the Pi Pico (Pin 3)
  7. Solder the Ground pin on the AT28C16 EEPROM to the Ground Pin on the Pi Pico (Pin 3)
  8. Solder the VCC pin (Pin 24) on the AT28C16 EEPROM to the VBus Pin on the Pi Pico (pin 40)
  9. Solder the unused AT28C16 EEPROM Address Pins (A8, A9 and A10) to a ground Pin on the Pi Pico.
  10. Solder the Chip Enable (CE) Pin (Pin 18 ) of the AT28C16 EEPROM to a ground Pin on the Pi Pico.
  11. Solder the Output Enable (OE) Pin (Pin 20 ) of the AT28C16 EEPROM to the VBus Pin on the Pi Pico (pin 40). This means it can only be written to and mot start trying to output data.
  12. Solder one side of the momentary push button to the 3.3V Out Pin on the Pi Pico (pin 36)
  13. Solder the other side of the momentary push button to GPIO Pin 28 on the Pi Pico (pin 34)
  14. Solder the Write Enable (WE) Pin (Pin 21 ) of the AT28C16 EEPROM to Pin 20 (GPIO 15) on the Pi Pico. This enables the data on the data line to be written to the selected to address when toggled low for 1mS.
  15. Solder your the AT28C16 EEPROM address Pins A0 to A7 (Pins 8 to 1) to the Pi Pico Address Pins (GPIO 7 to GPIO 0).
  16. Solder your AT28C16 EEPROM data Pins I/O-0 to I/O-7 to the Pi Pico data Pins (GPIO 16 to GPIO 22 and GPIO 26).

Once the soldering is complete you can plug your Pi Pico into the two 20 pin sockets and the EEPROM into the Zif socket. You will need to place the EEPROM at the top of the ZIF socket. Once in place, pull the leaver down to secure the I/C. You can now connect the USB lead between the Pico and your computer and start to program your EEPROM.

Programming and Testing

programmerPic3.jpg
Pi Pico EEPROM Programmer Video 4 - Test Circuit with 555 and 74HC393 #instructables
Pi Pico EEPROM Programmer Video 3 - Check program with XGeco TL866 II chip reader / writer
Pi Pico EEPROM Programmer Video 1 - Using Thonny and Micropython to program the Pico

I used the Thonny programming environment to program my Raspberry Pi Pico, I'm not going to go into detail about how to connect up the Pico and program it as there are many entry level videos available to demonstrate this. The attached video shows me programming the Pico and then running the program to get the data into the EEPROM.


I ran two tests to see if my EEPROM had been programmed correctly.

The first test I did was to put the EEPROM in my XGecu TL866 II programmer and read the program on the EEPROM.

The Second test was to build a circuit on a breadboard that iterates through the EEPROM addresses and outputs the data as blinking LEDs. This circuit uses a 555 IC used as a clock to pulse a 74HC393 4-stage Binary ripple counter. The two stages are linked so that is counts from 0-255 in binary (0000 0000 to 1111 1111). This gives the addresses to the EEPROM and 8 LEDs to show the selected address, the data from the EEPROM is then sent to another 8 LEDs, so the output can be seen.

The video's attached show my EEPROM testing.