To utilize this PMOD, my goal was to create an array of 8x8 pixel ASCII characters, that could be stored or updated as ASCII values. This could be converted from binary, hex, bcd, or scanned through something like UART. It would be very straightforward to convert the values into ASCII, as long as I could keep that ASCII array as a top level input and treat the OLED controller as a hands off display after it is tested. I would also like to be able to use inputs to change the color of the text and background, to make the displays more dynamic based on different states of the logic modules it works with.
As of now, the first semester of senior design has been completed. During this time, we were able to determine our design plan with doctor Duwe, research more about the tools we will be using, and begin testing submodules in our design. Our team decided on a modular design with multiple different functions, including a clock gating module, digital signals processing filter, an external SPI interface, and more. The intent of choosing a modular design was to push the capabilities of the fabrication process at the SkyWater foundry, and see how many working pieces we could receive on our digital ASIC through working with eFabless.
The following requirements are to be followed to interface with the OLED controller:
- The Master driven clock SCK shall have a frequency less than 6.66 MHz
- The Chip Select pin CS to the OLED slave controller must be pulled low before transmission
- Data on the MOSI line will be read on the positive edge of SCK on the OLED Controller
- A command code will force D/C to 0, while a data byte will force D/C to 1
- VCCEN and PMODEN must be set to 1 after an appropriate delay to turn on the OLED controller
- The command code 0xAF must be sent to power ON the display, followed by a 100ms wait
The above requirements are determined based on the reference manual for the SSD1331 OLED display controller and the schematic for the OLEDrgb breakout PMOD from Digilent. This would dictate the turn on sequence for the OLED controller, and lead me to designing a standard write only SPI module, which would handle driving the CS and MOSI pins.
The following requirements were determined by myself to achieve the goal of a fully controllable ASCII display:
- The full range of the display will be utilized, being 96 columns and 64 rows of pixels
- Each ASCII character will have 8 columns and 8 rows, or 64 total pixels
- The ASCII values will be stored as ASCII types, and converted to the 8x8 bit array through a case statement
- The ASCII values will be displayed one at a time, modifying the start and end addresses to display each 8x8 bit block in succession
- The ASCII characters will be displayed row by row, starting at column position 0 and incrementing to the right
- The ASCII text shall be one set color, and the ASCII background shall be another color, as inputs to the OLED Interface controller
The following set of modules were planned to be implemented, to achieve a fully customizable ASCII display in real time:
- Nbit_MOSI_SPI: A shift register will handle transmitting 1 byte of data over MOSI and driving CS, on the negative edge of SCK
- Nbit_MOSI_SPI_Buffer: Another module will load multiple bytes of data and D/C bits to send strings of commands in a row, updating on the positive edge of SCK
- OLED_interface: An OLED Controller module will be created to handle the turn on, turn off, and display states by writing to the byte buffer
- Ascii_font_8x8: Decodes 1 byte ASCII type into 8x8 bit array of pixels, with MSB being the top right pixel, LSB being the bottom left pixel, row by row
References
Documentation
The first module that was designed and tested was the Nbit_MOSI_SPI module, which acted as a shift register to output 1 byte over the MOSI SPI line. The FPGA would act as the master to the OLED controller, meaning it also had to control the CS pin. Once a transmit begun, the CS pin would be deasserted to 0 on the negative edge of the SCK clock cycle. Since the CS pin was active low, this indicated that on the next positive edge, the OLED controller would read the first MOSI bit and the D/C control. In the transmit state, once the second to last bit was transmitted over MOSI, a flag would be raised indicating the next bit would be the final bit sent in the byte. This would give the opportunity for another byte to be loaded while still staying in the transmit state, leading to one less clock cycle for every byte transmitted. Inputs such as i_START were included to control when the master would transmit, and would be driven by modules upstream. With this module, I was able to satisfy multiple requirements I had to follow and set for myself, including controlling the CS and MOSI pins of the SPI protocol.
The next module that was implemented and tested was the Nbit_MOSI_SPI_Buffer. This module used a similar counter and state machine to the bit level transfer mentioned before, but instead acted on the positive edge of SCK instead, ensuring timing constraints would be met between the two. This module worked to drive the START signal for the previous module, and would load multiple sets of bytes to continuously transmit over the MOSI output of the FPGA. This module would use the second to last bit flag to determine if another byte should be loaded based on a counter and the input number of bytes to transmit. Again, this was beneficial since it ensured constant output on the MOSI line without losing clock cycles in between waiting. Like before, a flag was added to indicate when the last byte was being transmitted, so the OLED controller module could determine when to load the next set of command or data bytes.
The OLED_Interface module is where everything came together. This would take in the multiple byte ASCII array of the entire display, and display it pixel by pixel. Before this could occur, the FPGA had to go through a turn on state, where the reset pin was applied, the power enable pins VCCEN and PMODEN were asserted, and the display ON command was set through the Nbit_MOSI_SPI_Buffer. This module included a clock divider to step down the 100MHz clock on the Basys3 to 5MHz, under the maximum frequency for the OLED Display. Another module was included, ascii_font_8x8, which was a case statement to decode 1 byte ASCII value to an 8x8 bit array that would contain the pixel display for each respective ASCII code. Each ASCII code would be displayed in the pixel_display states, where the first state would set the start and end row and columns to an 8x8 bit square, offset by the ASCII column and row counters. At the start of a new ASCII code, while the row and column limits were being set, a 64 bit register would store the converted ASCII code in the 8x8 bit pixel format. A counter and shift register were used to display all 64 pixels concurrently, each holding 1 byte of RGB data, determined by the text and background color inputs.
Finally, to apply the ASCII display to an old project, I decided to use the reaction timer module that I had made around December 2022. This timer was initially used with four seven segment displays, and would require you to press a button after an LED showed up, within one second. If you pressed the button before the LED showed up, or after one second, you would fail the game. This was able to be customized much more with the ASCII display, since I could change the background color to make the reaction easier, or indicate when a success or failure from the game occurred. This required me to create a module to convert hex values that were intended for a seven-segment display to ASCII characters, which could then be updated to the display in real time. I also added additional outputs from the reaction timer for the state of the machine and what the outcome of the game was, being either a success, early failure, or late failure. With this, I was able to use a new interface to enhance an old design.
Things that went well:
- SPI Write only interface implementation
- Notes from reference manuals and PMOD schematic
- Flexibility with generic parameters
Things that could improve:
- More documentation for end of project
- Could be more deliberate in state machines to avoid potential latches
- More automated testing would be nice
Things that were fun:
- Working through the state machine to display multiple ASCII characters
- Using RGB values to update text in real time
- Enhancing an old design with more accessibility and information