<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Goose</title>
    <description>The latest articles on DEV Community by Goose (@inn-goose).</description>
    <link>https://dev.to/inn-goose</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3461182%2Ff1fdfbd3-3a50-4396-b424-ad4dc1d2e2fb.png</url>
      <title>DEV Community: Goose</title>
      <link>https://dev.to/inn-goose</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/inn-goose"/>
    <language>en</language>
    <item>
      <title>EEPROM 28C64 API Performance with Arduino</title>
      <dc:creator>Goose</dc:creator>
      <pubDate>Sat, 30 Aug 2025 20:09:21 +0000</pubDate>
      <link>https://dev.to/inn-goose/eeprom-28c64-api-performance-with-arduino-2g20</link>
      <guid>https://dev.to/inn-goose/eeprom-28c64-api-performance-with-arduino-2g20</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/inngoose/eeprom-read-and-write-operations-with-arduino-2ll4"&gt;EEPROM Read and Write Operations with Arduino&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;In my previous post, I described a basic implementation of the EEPROM API and demonstrated, in a fairly simple example, how to read and write a few data cells. At that point, performance and operation speed were not particularly important, since the goal was only to provide an initial demonstration.&lt;/p&gt;

&lt;p&gt;Now I would like to focus on improving performance and, moreover, comparing the values from the EEPROM's datasheet with the actual operation speed. This should not really be considered a precise experiment on measuring the raw speed of the chip itself, because the Arduino platform inevitably introduces certain performance limitations by adding some overhead. Therefore, my goal is to make the read and write API operations as fast as realistically possible, while still taking into account the constraints of the platform.&lt;/p&gt;

&lt;p&gt;To achieve this, I will strictly follow the exact pin activation sequence described in the datasheet's waveforms and also respect the timing requirements for operations, such as the Write Cycle Time. I will eliminate unnecessary waiting functions and, in addition, use an oscilloscope to illustrate how the write waveforms actually look in practice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8q4j934g1svood44cuu6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8q4j934g1svood44cuu6.jpg" alt="Wiring for EEPROM and Oscilloscope" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Read Waveforms
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q9rp4qqqc8r3tnu6hp5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q9rp4qqqc8r3tnu6hp5.png" alt="EEPROM 28C64 Read Waveforms" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sequence of pin activations required for reading data from the EEPROM is, in fact, fairly straightforward.&lt;/p&gt;

&lt;p&gt;Read Operation Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the address on the &lt;code&gt;Address Bus&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable the Chip by setting &lt;code&gt;CE&lt;/code&gt; pin to &lt;code&gt;LOW&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable the Output by setting &lt;code&gt;OE&lt;/code&gt; pin to &lt;code&gt;LOW&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(*) Wait for the &lt;code&gt;OE&lt;/code&gt; to Output Delay Time&lt;/li&gt;
&lt;li&gt;Read the cell value on the &lt;code&gt;Data Bus&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Disable the Chip by setting &lt;code&gt;CE&lt;/code&gt; pin to &lt;code&gt;HIGH&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Disable the Output by setting &lt;code&gt;OE&lt;/code&gt; pin to &lt;code&gt;HIGH&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 4* is, interestingly, the one to watch, because Arduino platform specifics start to show. The EEPROM Datasheet states that the time between setting the &lt;code&gt;OE&lt;/code&gt; pin &lt;code&gt;LOW&lt;/code&gt; and the value appearing on the Data Bus is between 10 and 70 ns for my EEPROM model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4mzu8qxm4vn8uuhzumx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4mzu8qxm4vn8uuhzumx.jpeg" alt="Oscilloscope: Pin Write Delta" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, as the oscilloscope measurements indicate, the time between two pin write operations is about 120 ns. I can, reasonably, assume this is execution overhead, since the program is written in C and each function expands into a large set of low-level instructions. This delta between operations is sufficient for the value to appear on the Data Bus.&lt;/p&gt;

&lt;p&gt;The Arduino Giga clock is 240 MHz, which is 20 times higher than the Mega’s 16 MHz, so the delta between pin write operations may be 20 times higher. I would like to run measurements of this kind in one of the next articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write Waveforms
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthxi0expikx5l1r4p7lm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthxi0expikx5l1r4p7lm.png" alt="EEPROM 28C64 Write Waveforms" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The operations required for writing data are slightly more complex and require additional polling of the &lt;code&gt;READY/BUSY&lt;/code&gt; pin to determine when the write process has completed.&lt;/p&gt;

&lt;p&gt;Read Operation Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the address on the &lt;code&gt;Address Bus&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable the Chip by setting &lt;code&gt;CE&lt;/code&gt; pin to &lt;code&gt;LOW&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable the Write by setting &lt;code&gt;WE&lt;/code&gt; pin to &lt;code&gt;LOW&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set the value on the Address Bus (~1us, see below)&lt;/li&gt;
&lt;li&gt;Disable the Write by setting &lt;code&gt;WE&lt;/code&gt; pin to &lt;code&gt;HIGH&lt;/code&gt; (initiates the write)&lt;/li&gt;
&lt;li&gt;Disable the Chip by setting &lt;code&gt;CE&lt;/code&gt; pin to &lt;code&gt;HIGH&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(*) Poll for the &lt;code&gt;READY&lt;/code&gt; status&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The datasheet specifies two timing intervals important for implementing Chip Status Polling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time to Device Busy&lt;/strong&gt; – the time between the &lt;code&gt;WE&lt;/code&gt; pin rising edge and the &lt;code&gt;READY/BUSY&lt;/code&gt; pin transitioning to &lt;code&gt;BUSY&lt;/code&gt;, maximum 50 ns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write Cycle Time&lt;/strong&gt; – the time required to write data into the chip’s memory, maximum 1 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added a 1 µs wait for the &lt;strong&gt;Time to Device Busy&lt;/strong&gt;, though this is arguably unnecessary given the platform overhead for each operation.&lt;/p&gt;

&lt;p&gt;For the status polling procedure itself, I implemented a simple mechanism:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the chip is already in the &lt;code&gt;BUSY&lt;/code&gt; state at the start of polling, check repeatedly every 200 µs until it transitions to &lt;code&gt;READY&lt;/code&gt;, with a maximum timeout of 1.4 ms&lt;/li&gt;
&lt;li&gt;Alternatively, simply wait 1.4 ms using a standard &lt;code&gt;delay()&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;READY/BUSY&lt;/code&gt; status pin uses an &lt;strong&gt;Open Drain Output&lt;/strong&gt; connection, so it is necessary to set it to &lt;code&gt;INPUT_PULLUP&lt;/code&gt; mode for reading, or alternatively use an external pull-up resistor.&lt;/p&gt;

&lt;p&gt;I will cover the performance details of polling in the next section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4guag9x8ighozakrfog.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4guag9x8ighozakrfog.jpeg" alt="Oscilloscope: CE and WE waveforms" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The relationship between the &lt;code&gt;CE&lt;/code&gt; (yellow) and &lt;code&gt;WE&lt;/code&gt; (blue) pins, and their behavior during the write operation, is illustrated in the image above. First, the &lt;code&gt;CE&lt;/code&gt; pin is pulled &lt;code&gt;LOW&lt;/code&gt; to activate the chip. Then, the &lt;code&gt;WE&lt;/code&gt; pin is pulled &lt;code&gt;LOW&lt;/code&gt;, marking the start of placing a value on the Data Bus. The duration between the falling and rising edges of the &lt;code&gt;WE&lt;/code&gt; pin is about 1200 ns, which corresponds to roughly 8 write operations at 120 ns each, plus the overhead required to set the value on the bus. The rising edge of &lt;code&gt;WE&lt;/code&gt; initiates the actual data write process. The rising edge of &lt;code&gt;CE&lt;/code&gt; no longer affects the operation, as can be seen from the waveforms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvh0zfnyiuwodlqsn62f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvh0zfnyiuwodlqsn62f.jpeg" alt="Oscilloscope: WE to BUSY waveforms" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It turned out I misread the waveforms last time. &lt;code&gt;WE&lt;/code&gt; (blue) does indeed trigger the data-write operation on the rising edge, transferring the chip into the &lt;code&gt;BUSY&lt;/code&gt; state (yellow). This is confirmed by the oscilloscope measurements shown in the image above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqoc3ayx5h72dn5moliwp.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqoc3ayx5h72dn5moliwp.jpeg" alt="Oscilloscope: CE to BUSY waveforms" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setting the chip to inactive mode by driving the &lt;code&gt;CE&lt;/code&gt; pin &lt;code&gt;HIGH&lt;/code&gt; does not affect the data write process, as shown in the image above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polling &lt;code&gt;BUSY&lt;/code&gt; State
&lt;/h2&gt;

&lt;p&gt;Using active polling of the &lt;code&gt;READY/BUSY&lt;/code&gt; pin allows the write operation to be accelerated by roughly a factor of two.&lt;/p&gt;

&lt;p&gt;Oscilloscope measurements show that a write takes about 500 µs, compared to the specified maximum of 1000 µs. However, even waiting manually for the maximum period can occasionally result in write errors. If a new write cycle begins before the previous one has completed, it can corrupt the data from the previous cycle by placing new values on the Data Bus. For reliability, I therefore set the minimum wait time to 1400 µs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffalgedtzm6dq0lcmzb4j.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffalgedtzm6dq0lcmzb4j.jpeg" alt="Oscilloscope: READY delay" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1krvx8dhv70306bv6r4q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1krvx8dhv70306bv6r4q.jpeg" alt="Oscilloscope: READY poll" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Chip status polling, however, reduces the waiting time from 1400 µs to 600 µs without compromising write reliability. Two oscilloscope waveforms illustrate this difference. In the first image, the program simply waits 1400 µs. In the second image, the program actively polls the chip status and returns control from the write function as soon as the chip enters the &lt;code&gt;READY&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;This time could be further improved by reducing the &lt;code&gt;delay()&lt;/code&gt; between pin polls to 50 µs, which theoretically would accelerate the waiting procedure by a factor of three instead of two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/inn-goose/eeprom_arduino/tree/main/eeprom_performance" rel="noopener noreferrer"&gt;GitHub Project with the Performance API&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;old API implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WRITE | TOTAL: 8193044 us | AVG: 8001 us
READ | TOTAL: 15371674 us | AVG: 15011 us
VERIFY: OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;performance API implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUSY | TOTAL: 615502 us | AVG: 601 us | MAX: 605 us
WRITE | TOTAL: 622457 us | AVG: 607 us
READ | TOTAL: 4672 us | AVG: 4 us
VERIFY: OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I run a sequential block of write operations followed by a block of read operations. I write 1024 cells one by one with random values, storing the written values in an array. Then I read the same 1024 cells one by one, storing the read values in another array. During each write operation, I also record in an array the waiting time for the chip’s &lt;code&gt;READY/BUSY&lt;/code&gt; pin status, since this constitutes the main execution time consumption during writing.&lt;/p&gt;

&lt;p&gt;The results of an "old API implementation" are not useful for comparison, as the first API version was optimized for visual demonstration rather than speed. I prefer to compare results against the maximum expected values from the datasheet.&lt;/p&gt;

&lt;p&gt;I determined that a single digital pin read/write operation takes roughly 120 ns. During the write procedure, I perform 25 digital operations, giving 3000 ns, plus the array conversion procedure, resulting in about 4000 ns total, which aligns with the observed results. This process could be accelerated by caching the mapping between address values and bit representation, potentially speeding up single-cell reads by 5-10%. However, this is largely unnecessary, since the entire 8K cell address space can already be read in 40 ms.&lt;/p&gt;

&lt;p&gt;For write operations, the picture is similar regarding execution overhead. The average operation execution time is 607 µs, with an average &lt;code&gt;READY/BUSY&lt;/code&gt; pin polling wait of 601 µs, leaving about 6 µs for Arduino-side operations. This is slightly higher than for reading, due to data conversion and a 1 µs &lt;code&gt;delay()&lt;/code&gt; before polling. The polling process itself could be sped up by checking the status every 20 µs, reducing total wait time by roughly 100 µs.&lt;/p&gt;

&lt;p&gt;The values achieved for read operations are very good, as the datasheet specifies a maximum wait of 1 ms, whereas I achieve 0.6 ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Verification
&lt;/h2&gt;

&lt;p&gt;After completing the write and read blocks, I compare the arrays to match data and check for errors. If writes are performed too quickly, operations overlap and data integrity suffers. For example, reducing the &lt;code&gt;READY&lt;/code&gt; wait time from 1.4 ms to 0.2 ms results in almost all cells being written incorrectly.&lt;/p&gt;

&lt;p&gt;This data verification is unrelated to the data corruption discussed in my previous article, since here I write and read within a single operational cycle. Detecting potential memory degradation of the chip requires performing a power cycle between write and read operations, which will be the topic of one of my upcoming articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;(1) Check how many write cycles the new EEPROM chip supports and examine how its write endurance degrades, whether this manifests as slower write speeds or as data corruption.&lt;/p&gt;

&lt;p&gt;(2) Write a program to verify the data stored on the EEPROM chip. Additionally, test data retention over a 24-hour period (the datasheet specifies 10 years).&lt;/p&gt;

&lt;p&gt;(3) Assemble a breadboard setup with a ZIF socket to allow quick replacement of EEPROM chips for validation and verification.&lt;/p&gt;

&lt;p&gt;(4) Compare the overhead of &lt;code&gt;digitalWrite()&lt;/code&gt; and &lt;code&gt;digitalRead()&lt;/code&gt; operations on Arduino Mega versus Arduino Giga, taking into account the 20× difference in clock speed.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>eeprom</category>
      <category>eeprom28c64</category>
    </item>
    <item>
      <title>EEPROM Read and Write Operations with Arduino</title>
      <dc:creator>Goose</dc:creator>
      <pubDate>Wed, 27 Aug 2025 10:38:11 +0000</pubDate>
      <link>https://dev.to/inn-goose/eeprom-read-and-write-operations-with-arduino-2ll4</link>
      <guid>https://dev.to/inn-goose/eeprom-read-and-write-operations-with-arduino-2ll4</guid>
      <description>&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;I decided to dig into how computers actually work at the very lowest level. To do that, I chose to take individual components and perform various operations that are described for each component in its datasheet. Studying the parts of older computers is much easier, because thirty years ago each function had its own dedicated component or a small group of components, and I can study them one by one.&lt;/p&gt;

&lt;p&gt;I started with &lt;a href="https://en.wikipedia.org/wiki/EEPROM" rel="noopener noreferrer"&gt;EEPROM&lt;/a&gt;, since it is a fairly straightforward component that is responsible for static data storage, and it really supports only two operations: read and write. The pin structure of the chip is also very simple: there is one address bus, one data bus, and just a few pins for controlling the operating mode.&lt;/p&gt;

&lt;p&gt;For my experiments I’m using an Arduino, because it allows me to recreate an artificial environment where the component is supposed to work without too much hassle. I picked a specific chip and wrote a small API class for it to work with Arduino.&lt;/p&gt;

&lt;p&gt;The chip model is 28C64, which is a static EEPROM with digital rewrite capability. It has 28 pins, and I’m using an Arduino Giga, since the standard Uno model does not have enough pins available. The data bus width is 8 bits, which means that it writes one byte into a single cell. The address bus width is 13 bits, so in total it can store 8,192 words.&lt;/p&gt;

&lt;p&gt;In this article I’ll walk through how the basic operations actually work and also highlight some of the subtle aspects of the chip’s behavior that might not be reflected in the datasheet. That’s exactly why I decided to check how well the documented operations match the real conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Datasheet
&lt;/h2&gt;

&lt;p&gt;Key points I can read from the &lt;a href="https://ww1.microchip.com/downloads/en/devicedoc/doc0001h.pdf" rel="noopener noreferrer"&gt;28C64 Datasheet&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The pin diagram, which helps us connect the necessary pins to the Arduino&lt;/li&gt;
&lt;li&gt;The required voltage, which in our case is 5V, the same as Arduino&lt;/li&gt;
&lt;li&gt;The behavior of the control pins, meaning which function is triggered by a positive or negative signal&lt;/li&gt;
&lt;li&gt;The types of supported operations — at this stage I only care about reading and writing a single byte&lt;/li&gt;
&lt;li&gt;The sequence of pin activations required to perform an operation (waveforms)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this initial stage I am not concerned with tolerances, error handling, or performance characteristics, I just wanted to validate the basic functions of the chip.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadnogiscdhmtyco5602f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadnogiscdhmtyco5602f.png" alt="EEPROM 28C64 Pins Layout" width="774" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  EEPROM API for Arduino
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Eeprom28C64Api&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="n"&gt;Eeprom28C64Api&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;pins&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="p"&gt;...);&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// init pins&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;readInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// init pins for read mode&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;readData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;writeInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// init pins for write mode&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;writeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrote a simple Arduino library that hides the low-level interaction with the chip behind just a couple of read and write functions. Here I’d like to go over some details of the implementation.&lt;/p&gt;

&lt;p&gt;The constructor defines how the chip’s pins are connected to the Arduino. This wiring is unique for each board, in my case I connected the pins in a certain order, but if you decide to repeat the experiment, your layout may look quite different. After that you need to explicitly call &lt;code&gt;init()&lt;/code&gt; method, which activates the Arduino pins for either input or output to the chip, and only then you can start reading and writing data.&lt;/p&gt;

&lt;p&gt;Two separate types of functions are defined for read and write operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initialization, which sets the Arduino pins into the correct state. For example, the data pins can be used for both reading and writing, and the mode depends on the microcontroller settings.&lt;/li&gt;
&lt;li&gt;The actual operation, which reproduces the sequence of toggling between LOW and HIGH signals on the pins, exactly as shown in the waveform diagram.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the time of writing this article, the library was not optimized for high-speed operation with the chip. Long delays are used between operations, and the status pin is not used at all. I plan to cover optimization and the chip’s performance characteristics in a separate article.&lt;/p&gt;

&lt;p&gt;(*) Note that Arduino Uno is not suitable, since it lacks enough digital pins. It has only 14, while you need 24 to connect the address bus, the data bus, and the control pins. So the minimum workable option here is Arduino Mega.&lt;/p&gt;

&lt;p&gt;(**) A project with a usage example can be found on my GitHub: &lt;a href="https://github.com/inn-goose/eeprom_arduino/tree/main/eeprom_basic_rw" rel="noopener noreferrer"&gt;EEPROM Basic RW&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Arduino and EEPROM Wiring
&lt;/h2&gt;

&lt;p&gt;I used half of a breadboard to place the EEPROM chip. On the same breadboard, I also added a control button and then connected everything to the Arduino. After that, I created an &lt;code&gt;Eeprom28C64Api&lt;/code&gt; class object, specifying which pins I used for which functions.&lt;/p&gt;

&lt;p&gt;White wires are used for the address bus, red wires for the data bus, and colored wires for the control pins.&lt;/p&gt;

&lt;p&gt;The button controls the program’s execution loop, switching between read and write modes. First comes the read mode: the program reads ten memory cells one by one with a half-second interval. After that, the button press enables the write mode, where the program writes ten memory cells with a half-second interval. Then it reads again, and the modes are looped, basically.&lt;/p&gt;

&lt;p&gt;I use a I2C 2004 LCD screen to display the status of the current operation, the address, and the data. Using this screen is not strictly necessary, but I found it convenient to have both control (the button) and display (the LCD) in one place, without relying on the Arduino console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsv0wb0tbhcdi8l9wzm7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsv0wb0tbhcdi8l9wzm7.jpg" alt="EEPROM 28C64 wiring with Arduino" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgulsf3b1bhp0av412oma.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgulsf3b1bhp0av412oma.jpg" alt="EEPROM 28C64 on Breadboard" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bits to Bytes Conversion Logic
&lt;/h2&gt;

&lt;p&gt;I use a closed ecosystem, so I can convert bit arrays into numeric values in any order I choose. I follow the &lt;a href="https://en.wikipedia.org/wiki/Bit_numbering#Most-_vs_least-significant_bit_first" rel="noopener noreferrer"&gt;Most Significant Bit First&lt;/a&gt; ordering, where the least significant bit represents the minimum value and the most significant bit corresponds to the maximum value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Read Operation and Sequence
&lt;/h2&gt;

&lt;p&gt;The sequence for executing a read operation follows the waveforms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the &lt;code&gt;address&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set the control pins such as &lt;code&gt;–CE&lt;/code&gt;, &lt;code&gt;–OE&lt;/code&gt;, and &lt;code&gt;+WE&lt;/code&gt; (not shown in the diagram)&lt;/li&gt;
&lt;li&gt;Read the &lt;code&gt;data&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q9rp4qqqc8r3tnu6hp5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5q9rp4qqqc8r3tnu6hp5.png" alt="EEPROM 28C64 Read Waveforms" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1npzof7jin1quhxtwunr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1npzof7jin1quhxtwunr.jpg" alt="EEPROM 28C64 Read operation with Arduino" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Write Operation and Sequence
&lt;/h2&gt;

&lt;p&gt;The datasheet shows two waveforms for writing: the first uses &lt;code&gt;!WE&lt;/code&gt; for control, the second uses &lt;code&gt;!CE&lt;/code&gt;. In both cases, the data bits are set after the control pins go &lt;code&gt;LOW&lt;/code&gt;. However, the operation description states that data is written on the rising edge.&lt;/p&gt;

&lt;p&gt;In practice, I could not reproduce this behavior. If I first set the control pins &lt;code&gt;LOW&lt;/code&gt;, then set the data, and finally bring the control pins &lt;code&gt;HIGH&lt;/code&gt; (rising edge), the data written to the cell corresponds to what was on the data bus at the moment of the falling edge. This leads me to hypothesize that the waveform diagram is incorrect, and the actual write trigger is the transition to &lt;code&gt;LOW&lt;/code&gt; on the control pins.&lt;/p&gt;

&lt;p&gt;I will investigate this discrepancy further when studying performance and attempting to achieve the specified 120 nanoseconds per read operation.&lt;/p&gt;

&lt;p&gt;Thus, in my case the working sequence is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the &lt;code&gt;address&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;data&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Send a &lt;code&gt;LOW&lt;/code&gt; signal to the control pins as &lt;code&gt;–CE&lt;/code&gt; and &lt;code&gt;–WE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Wait 1 millisecond&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthxi0expikx5l1r4p7lm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthxi0expikx5l1r4p7lm.png" alt="EEPROM 28C64 Write Waveforms" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92kxrdx34cq223osyq7y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92kxrdx34cq223osyq7y.jpg" alt="EEPROM 28C64 Write operation with Arduino" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary / Next Steps
&lt;/h2&gt;

&lt;p&gt;The chip behaves as expected, writing data to memory and allowing it to be read afterward. Questions about performance and the accuracy of the write operation remain open. I will address these issues in future articles in the EEPROM and Arduino series. For that, I will need readings from the &lt;code&gt;READY/!BUSY&lt;/code&gt; pin with an oscilloscope.&lt;/p&gt;

&lt;p&gt;Additionally, data verification raises an interesting point. It seems that the chip I am using cannot reliably retain data at the beginning of the address space after a power cycle. I wrote numbers 0–9 to the first ten addresses, but after a power cycle, reading these cells returns random values. However, using cells from the middle of the address space does not show this problem.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>eeprom</category>
      <category>eeprom28c64</category>
    </item>
  </channel>
</rss>
