How to use a 0.96 inch OLED with a FPGA?
How to Use a 0.96 Inch OLED with a FPGA
To use a 0.96 inch OLED with an FPGA, you need to connect the display via SPI or I2C, write a driver in Verilog or VHDL, and control pixel data through a frame buffer. The typical 0.96 inch 128x64 spi i2c oled display uses the SSD1306 controller, which operates at 3.3V logic levels and draws about 20mA during full-on operation. FPGAs like the Xilinx Spartan-6 or Intel Cyclone IV can interface directly with these displays, but you must handle timing constraints, initialization sequences, and memory management. The OLED panel has a resolution of 128x64 pixels, monochrome, with a pixel pitch of 0.21mm. It supports both SPI (up to 10 MHz) and I2C (up to 400 kHz) interfaces, but SPI is faster and easier for FPGA integration because it avoids the complexity of I2C addressing and clock stretching. The display module includes a built-in DC-DC converter, so it requires a single 3.3V supply, but the internal charge pump generates up to 12V for the OLED pixels. When driving from an FPGA, you must ensure the I/O pins are 3.3V tolerant; many FPGAs have 3.3V banks, but if you use a 5V-tolerant pin, you need a level shifter. The SSD1306 has a 1KB internal RAM (128x64 bits = 8192 bits), which is exactly 1KB. You can either write pixel data directly to this RAM via SPI commands, or use an external frame buffer in the FPGA’s block RAM (BRAM) for double-buffering. For a 128x64 monochrome display, you need 8,192 bits of BRAM, which is about 1KB. Many FPGAs have plenty of BRAM: a Spartan-6 LX9 has 32KB, a Cyclone IV EP4CE6 has 18KB. So you can allocate a 1KB buffer easily. The key is to map the pixel coordinates to the SSD1306’s memory layout, which is organized in pages (8 pages, each 128 bytes). Each page corresponds to 8 vertical rows of pixels. So pixel (x, y) maps to page = y/8, column = x, and bit position = y%8. This is non-intuitive, so you need to write a pixel-setting function that calculates the correct byte and bit mask. For example, to set pixel (10, 20), page = 20/8 = 2, column = 10, bit = 20%8 = 4. You then read the byte at page 2, column 10, set bit 4, and write it back. Or you can maintain a full frame buffer in the FPGA and update the display only when the buffer changes. The SSD1306 supports hardware scrolling, but for custom graphics, you’ll want to send full frame updates. The SPI interface uses 4 wires: CS (chip select), DC (data/command), SCK (clock), and MOSI (data). The FPGA must drive CS low, then send a command byte (DC low) or data byte (DC high). The SSD1306 expects commands in a specific sequence: first, turn off the display (0xAE), set display clock divide ratio (0xD5, 0x80), set multiplex ratio (0xA8, 0x3F for 64 rows), set display offset (0xD3, 0x00), set start line (0x40), enable charge pump (0x8D, 0x14), set memory addressing mode (0x20, 0x00 for horizontal mode), set segment re-map (0xA1), set COM scan direction (0xC8), set COM pins hardware configuration (0xDA, 0x12), set contrast (0x81, 0xCF), set pre-charge period (0xD9, 0xF1), set VCOMH deselect level (0xDB, 0x40), set display on (0xAF). This sequence is critical; missing a step can cause a blank screen. You can hardcode this sequence in a Verilog state machine. The FPGA must generate a clock for the SPI interface. For a 10 MHz SPI clock, you can divide the system clock (e.g., 50 MHz) by 5. The data rate is 10 Mbps, so a full frame of 8,192 bits takes 819.2 µs, which is fast enough for 60 fps (16.6 ms frame time). But if you update the entire frame every time, you’ll use about 5% of the SPI bandwidth. The I2C interface is slower: at 400 kHz, a full frame takes 20.48 ms, which is barely 48 fps. So SPI is preferred for FPGA projects. The initialization sequence takes about 200 µs, so you can start the display in less than 1 ms after power-up. The SSD1306 also has a built-in oscillator, so you don’t need an external clock. The FPGA must handle the power-on reset: the display has a power-on reset circuit, but you can also send a software reset command (0xE3). The display’s operating voltage range is 3.0V to 3.5V, and the absolute maximum is 3.6V. If your FPGA uses 3.3V, it’s fine. But if you use a 5V FPGA (like some older models), you need a level shifter like the 74LVC125. The OLED display has a lifetime of about 10,000 hours at full brightness, but you can reduce contrast to extend it. The contrast register (0x81) allows values from 0x00 to 0xFF. For typical indoor use, 0x80 is fine. The display also has a power-saving mode: you can turn off the charge pump (0x8D, 0x10) to reduce power consumption to under 1mA, but the display will be blank. The pixel brightness is uniform across the panel, but the OLED material ages faster at higher temperatures. The display’s operating temperature range is -40°C to +85°C, so it’s suitable for industrial applications. For FPGA integration, you need to consider the physical connections: the display module usually comes with a 4-pin header for SPI (CS, DC, SCK, MOSI) or a 4-pin for I2C (VCC, GND, SCL, SDA). Some modules have a 7-pin header that supports both interfaces. You can select the interface by tying the CS pin to GND for I2C or to VCC for SPI. But many modules have a resistor jumper on the back. Check the datasheet. The display module’s dimensions are 27.5mm x 27.5mm x 4.5mm, with a viewing area of 21.7mm x 11.2mm. The pixel size is 0.15mm x 0.15mm, with a gap of 0.05mm. The contrast ratio is typically 2000:1, and the response time is under 10µs. The display is reflective, so it’s readable in sunlight without backlight. The SSD1306 controller also supports horizontal and vertical scrolling, but you can implement custom scrolling in the FPGA by shifting the frame buffer. The display’s memory is SRAM, so it retains data as long as power is applied. If you want to display text, you need a font generator. For a 5x7 font, each character is 5 bytes wide. You can store a character map in the FPGA’s BRAM. For the full ASCII set (95 characters), you need 95 * 5 = 475 bytes, which is trivial. For a 128x64 display, you can fit 128/5 = 25 characters per row, and 64/7 = 9 rows, so 225 characters total. But you need to handle the page mapping. A common approach is to use a framebuffer in the FPGA’s BRAM, then write a Verilog module that reads the buffer and sends SPI commands. The module can be a state machine with states: IDLE, INIT, WRITE_FRAME, and SCROLL. The INIT state sends the 25-byte initialization sequence. The WRITE_FRAME state sends 8,192 bits of pixel data. The SCROLL state can be omitted if you don’t need scrolling. The FPGA must also handle the display’s busy flag? The SSD1306 doesn’t have a busy pin; you just send commands at the SPI clock rate. But you must ensure the SPI clock is not too fast; the datasheet specifies a maximum of 10 MHz. If you exceed that, the display may ignore commands. The display’s input capacitance is about 10pF, so the SPI lines should be kept short (under 10cm) to avoid signal degradation. The FPGA’s output impedance is typically 50 ohms, so you can drive the display directly. But if you use long wires, you might need series resistors (e.g., 22 ohms) to dampen reflections. The display’s power supply should be decoupled with a 10µF electrolytic and a 0.1µF ceramic capacitor near the module. The FPGA’s power supply should be separate to avoid noise coupling. The OLED display’s current consumption is about 20mA with all pixels on, but typical use (text or graphics) is about 10mA. The FPGA’s I/O pins can source up to 24mA per pin, so it’s fine. But the total current from the FPGA’s bank should not exceed 100mA. The display’s logic input thresholds are 0.3*VCC for low and 0.7*VCC for high. At 3.3V, the low threshold is 0.99V, high is 2.31V. FPGA outputs at 3.3V are typically 3.0V minimum, so they meet the threshold. The display’s output (if any) is only for I2C data, but in SPI mode, there is no data out from the display. So you don’t need bidirectional pins. The FPGA’s Verilog code can be written in a simple module with input clk, reset, and output cs, dc, sck, mosi. You can use a 16-bit counter to generate the SPI clock. The state machine can be implemented with a 4-bit state register. The initialization sequence can be stored in a ROM table. The pixel data can be read from a BRAM that is written by another module (e.g., a graphics engine). The graphics engine can draw lines, circles, or text. For a simple test, you can fill the display with a checkerboard pattern. The checkerboard pattern is easy: for each pixel (x, y), if (x/8 + y/8) % 2 == 0, set pixel to 1, else 0. This creates 8x8 blocks. The pattern can be generated in the FPGA using combinational logic. The display’s response time is under 10µs, so you can update the frame at 100 Hz without ghosting. The OLED’s persistence is excellent, so no flicker. The display’s viewing angle is 160 degrees, so it’s readable from any angle. The contrast ratio is 2000:1, so text is sharp. The display’s reflectivity is about 5%, so it’s not a mirror. The display’s thickness is 4.5mm, so it’s thin. The display’s weight is 5g, so it’s light. The display’s connector is a 4-pin through-hole header with 2.54mm pitch. You can solder wires directly or use a female header. The FPGA’s development board usually has a 40-pin header. You can connect the display’s pins to the FPGA’s GPIO pins. For example, on a Digilent Basys 3 board, you can use PMOD pins. The Basys 3 has a 12-pin PMOD connector with 3.3V output. You can map CS to PMOD[0], DC to PMOD[1], SCK to PMOD[2], MOSI to PMOD[3]. The display’s VCC and GND connect to the board’s 3.3V and GND. The Basys 3’s FPGA is a Xilinx Artix-7, which has 3.3V I/O banks. The Artix-7 has 240 I/O pins, so you have plenty. The Basys 3 also has a 100 MHz clock, which you can divide to 10 MHz for SPI. The Verilog code can be synthesized in Vivado. The resource usage is minimal: about 100 LUTs and 1 BRAM for the frame buffer. The code can be written in a single .v file. The testbench can simulate the SPI waveform. The display’s initialization sequence can be verified with a logic analyzer. The display’s performance can be measured with a photodiode. The display’s temperature can be measured with a thermocouple. The display’s lifetime can be tested with an accelerated aging test. The display’s pixel degradation can be measured with a luminance meter. The display’s uniformity can be measured with a camera. The display’s color temperature is not relevant because it’s monochrome. The display’s spectral output is in the blue region (peak at 460nm). The display’s brightness is 100 cd/m² typical. The display’s dimming can be controlled by the contrast register. The display’s power consumption is proportional to the number of lit pixels. The display’s efficiency is about 10 lm/W. The display’s driver IC is the SSD1306, which is manufactured by Solomon Systech. The SSD1306 is a common driver for small OLEDs. The SSD1306 supports 128x64, 128x32, and 96x16 resolutions. The 0.96 inch version is 128x64. The SSD1306 has a 1KB SRAM. The SSD1306 supports SPI, I2C, and 6800/8080 parallel interfaces. The parallel interface is faster but uses more pins. For FPGA, SPI is the easiest. The SSD1306’s command set is documented in the datasheet. The commands include: Set Contrast (0x81), Display ON/OFF (0xAF/0xAE), Set Memory Addressing Mode (0x20), Set Column Address (0x21), Set Page Address (0x22), Set Display Start Line (0x40), Set Segment Re-map (0xA0/0xA1), Set Multiplex Ratio (0xA8), Set COM Output Scan Direction (0xC0/0xC8), Set Display Offset (0xD3), Set Display Clock Divide Ratio (0xD5), Set Pre-charge Period (0xD9), Set COM Pins Hardware Configuration (0xDA), Set VCOMH Deselect Level (0xDB), Enable Charge Pump (0x8D). The initialization sequence must be sent in order. The display will not work if the sequence is wrong. The sequence can be stored in a lookup table. The table can be implemented as a case statement in Verilog. The case statement can have 25 entries. Each entry is a command byte and a data byte (if needed). The state machine can iterate through the table. The table can be hardcoded. The FPGA’s clock frequency determines the SPI clock. For a 50 MHz clock, you can divide by 5 to get 10 MHz. The SPI clock can be generated by a counter. The counter counts from 0 to 4. At count 0, set SCK low. At count 2, set SCK high. At count 4, set SCK low. The MOSI data is set at the falling edge of SCK. The CS is active low. The DC is set to 0 for command, 1 for data. The FPGA’s reset signal can be connected to a button. The display’s power-on reset is automatic, but you can also send a software reset. The software reset command is 0xE3. The display will reset in 100µs. The display’s internal oscillator is 400 kHz. The display’s frame rate is 100 Hz default. The display’s refresh rate is 100 Hz. The display’s pixel update rate is 10 MHz. The display’s memory is static. The display’s data retention is indefinite. The display’s operating life is 10,000 hours. The display’s storage life is 10 years. The display’s humidity range is 5% to 95% non-condensing. The display’s vibration resistance is 10G. The display’s shock resistance is 100G. The display’s ESD rating is 2kV. The display’s RoHS compliance is Yes. The display’s package is a 4-pin SIP. The display’s pinout is: 1: VCC (3.3V), 2: GND, 3: SCL/SCK, 4: SDA/MOSI. Some modules have a 5th pin: RESET. The reset pin is active low. You can connect it to the FPGA’s GPIO. The reset pin can be used to reset the display. The display’s reset timing is 100ns. The display’s power-up sequence: apply VCC, wait 100ms, then send commands. The display’s power-down sequence: send display off command, then remove VCC. The display’s standby current is 1µA. The display’s sleep mode current is 0.1µA. The display’s wake-up time is 100µs. The display’s brightness can be adjusted by PWM. The SSD1306 does not have a PWM input, but you can adjust the contrast register. The contrast register controls the charge pump voltage. The voltage ranges from 7V to 12V. The brightness is proportional to the voltage. The contrast register value 0x00 gives minimum brightness, 0xFF gives maximum. The brightness is linear with the register value. The brightness is also affected by the pre-charge period. The pre-charge period register (0xD9) controls the duration of the pre-charge phase. The default value is 0xF1. You can increase it to 0xFF for higher brightness, but at the cost of power. The pre-charge period is 2 to 15 clock cycles. The clock is the internal oscillator. The pre-charge period affects the pixel charging
Stop guessing. Start training like a ranked fighter.
Working fighters, prospects, and coaches use Keith's systems to cut camp planning in half and step into the cage prepared.
Book a Fight-Camp Consult