Sunday, June 18, 2023

Drawing on the LCD screen to test functions I need - part 7

FOUND THE PROBLEM AFFECTING THE LCD MODULE

As a quick test I wired one of my new LCD Modules instead of the original one, simply to eliminate the chance that some hardware failure on the board was causing the problem. The behavior was identical. That does point me back at my DE10-Nano either in the Verilog for the FPGA side or the C code on the Linux HPS side. 

Up next was a more complete oscilloscope examination of the signal lines. Previously I had monitored the three SPI function output signals (SCLK, MOSI and SS) as well as my software driven pseudo Slave Select for the LCD module. Those worked properly.

Other signals essential to the operation of the LCD Module are LCD Reset, LCD Data/Command Mode, and Touch Select. First up was the reset since a faulty reset, held low, would keep the LCD controller chip from responding. A clean initial reset and then steady unasserted (high) levels were seen. Next was the LCD Data/Command Mode which when low tells the controller chip that a command is being sent, while high the controller expects parameters or other data. 

That signal was frozen in the high position. It never emitted a low state, which it must in order for the controller to reset, turn on and accept the other commands I was sending. That output would completely explain why the screen remained dark. 

This line is controlled by writing bits to the memory block in Linux that is transmitted to the FPGA's PIO output module, that in turn outputs the bit on the external I/O pin. This same method is used to send the reset command, which worked, and to send the LCD Select pseudo-SS which worked, thus the issue must be some subtle flaw. 

I did find it! There was a defined constant in my code that was wrong, thus I wasn't toggling the LCD Data/Command Mode bit when I thought I was. This was fixed in a trice and the screen came to life. I discovered a few issues with the user interface but can write what I want on the screen which is what is important.

ISSUES I SPOTTED IN THE FIRST TEST WRITING ON THE SCREEN

The first thing I attempted was to write a text string to the X and Y address 0,0 in order to see whether the letters are cut off at the edges. The text was sideways starting from the bottom left corner, not in the orientation I expected. 

The second observation was that 8 point font is very tiny indeed which will be hard for a user to read when they are selecting virtual cartridge file names. I will need to increase the size, which limits the number of file names on the screen as well as the maximum length of a name I can display. 

There was an error in how I used the GUI function to write the strings, which I attempted to use to list the file names found on the SD card. I received out of range errors, which I think means that I have to set the window boundaries properly or pass proper parameters. Same with the arrow bitmaps. 

Finally I drew a rectangle on the screen. I believe I have the position and orientation wrong, similar to how the text was misoriented with the earliest strings I wrote. 

PLAN TO CORRECT THE DISCOVERED FLAWS

I will chase down the reason for the strange orientation of the objects on the screen, then correct it. That should cause my text to run from left to right, with the lines going from top down to bottom. That should also put the rectangle in a better place.

I will experiment with larger fonts, finding the smallest that is acceptable and then sorting out the number of lines and max name size based on that. This will change a fair amount of the user interface code. 

Friday, June 16, 2023

Reorganization of signals to improve cabling signal integrity

RIBBON CABLE LINKING DE10-NANO, LCD MODULE AND MY 1130 INTERFACE BOARD

The DE10-Nano general purpose IO connectors are 2.54mm 2 x 20 connectors, very like PC disk and floppy connectors, using ribbon cable to carry signals between the ends. I will use the ribbon cables to bring the signals over to the other two boards but they will not work plugging in an identical connector so I have some wiring to accomplish. 

The LCD Module end does not use the same connector, instead it has a space designed to fit a Raspberry Pi Pico. That is similar to a Dual Inline Package but they separated the two rows of 20 pins by an additional 2.54mm thus incompatible with a DIP socket. 

My interface board does use the same 2.54mm 2 x 20 connector however my pin assignments vary. I also use the cable to carry some voltages which I don't want to connect to the DE10-Nano. This means I will be modifying the connections from the cable slightly. Same basic order of the signals, just a few places where the cable is split to skip over pins or slightly realign. 

KiCAD view of the board design

GROUND BETWEEN EACH SIGNAL WIRE TO REDUCE COUPLING

The goal is to minimize signals coupling (being induced in adjacent wires). With ribbon cables it is common to have a ground wire between each signal wire to accomplish this. I thus reassigned the signals on the DE10-Nano connector to allow the signals to be sandwiched between grounds. 

DIVISION OF 1130 ORIENTED AND LCD MODULE ORIENTED SIGNALS BY CONNECTOR

I have signals that connect this product to the IBM 1130 disk drive. Other wires are hooked to the LCD Module to support the user interface with an LCD panel and a touch screen atop it. For simplicity of cabling and routing of wires, I separated these to their own cables. The DE10-Nano has two GPIO connectors, thus I used GPIO 0 for the 1130 related signals and GPIO 1 for the LCD Module wiring. 

MODIFICATION OF WIRING OF MY 1130 INTERFACE BOARD 

My earlier approach to building this product used an Arduino Mega with an LCD daughter card for the user interface and the SD Card where the virtual card images resided, communicating via SPI to a Xilinx S7 based FPGA board, but the toolchain was causing me major grief. That was when I changed horses to the Altera/Intel based DE10-Nano and its more stable toolchain. 

However, the Arduino Mega 2560 board used 5V signaling levels, while the FPGA board was based on 3.3V levels and the IBM 1130 system's SLT is a 3V scheme. To convert voltage levels for signals passing between the boards I developed that interface board which handled 3V, 3.3V and 5V signals. For example, a signal between Arduino and FPGA would convert 5V to 3.3V or vice versa. 

My DE10-Nano and the LCD Module are all based on 3.3V, which changes the voltage levels involved in certain signals. I looked over my board design and schematic and came to the realization that my only change is to connect the power pin for 5V to the 3.3V source, which would cause all the converters to properly shift levels between 3V and 3.3V as needed. 

ADDITIONAL DE10-NANO AND LCD MODULES ORDERED

I expect to build and install a few more of the Virtual 2315 Cartridge Facility products on other IBM 1130 systems. Currently another museum is planning to have me restore their 1130 to operation and its usability will be greatly enhanced by adding one of these to it. The National Museum of Computing in the UK has a running IBM 1130 which could make use of one of these as well.

I picked up another DE10-Nano and some LCD Modules so that I have the key components to build more of these systems. 

Sunday, June 11, 2023

Drawing on the LCD screen to test functions I need - part 6

CORRECTING INITIALIZATION TO ALLOW DRAWING ON THE SCREEN

I found a simple error in my porting changes to the function that should have been initializing the screen dimensions. It is now corrected and the previous error situations in routines to clear screens or draw objects have been rectified. 

DEEPER DIVE INTO THE SEQUENCE INITIALIZING THE LCD CONTROLLER CHIP

While I feel that the code is correctly sending commands, that is 8 bit transmissions with the proper control signal activated, I have not fully checked how this is handling data which is either a single frame of 16 bits bounded by my LCD Select or multiple 16 bit words sent under a single LCD Select session. If either of these is not operating correctly then it won't initialize properly. 

I did monitor such sequences and was satisfied that the LCD Select, my falsified slave select to the LCD Module, is working as intended to clock in 8 or 16 bits, triggering the LCD controller chip to accept the command or data, as well as toggling as each 16 bit word arrives in a stream of them. 

GAINING UNDERSTANDING OF HOW TO WRITE TO THE LCD SCREEN

The LCD controller has a memory that is 480 x 320 pixels in size, each pixel being a 16 bit color quantity. The controller can scan through the memory in multiple ways. It can advance horizontally along a line from left to right, then drop down to the next line which again runs left to right. This is called L2F U2D mode. There are modes that scan right to left, modes that bottom to top, and one can instead scan down or up columns first, then advance horizontally to the next column either right or left. Many combinations of modes exist. 

Commands configure the mode of the controller chip. For my purposes I will always operate in the L2R U2D mode. The method of transmitting pixels to the screen in any mode involves starting or restarting a write to memory using commands 0x2C (and 0x3C). Every data word following that command up until the next command byte is received will be entered into the memory, left to right rows moving from top to bottom. 

The decision for when the right end of a line is reached, so that the next pixel is put in the leftmost position one row down, is based on a window that is set up by commands. The command 0x2A defines the leftmost and rightmost coordinate of the window. If that is set to 0 and 479, for example, then when we stream in pixels they will start at the left edge of the screen and continue all the way to the right edge. If we instead issue 0x2A with an extent of 100 and 199, then we stream pixels in which are placed starting at X position 100, continuing to X position 199, then back to X position 100 on Y position + 1. 

Similarly, the command 0x2B will set the top and bottom coordinates of the window. If set to 0 and 319, then when we start streaming pixel data it begins on the top edge of the screen and after reaching the right end of a line, drops down 1 line until finally reaching the bottom edge. If we instead issue 0x2B with an extent of 50 and 59, then our first pixel is on the left edge of the window at Y coordinate 50 and once the pixels reach the right edge of the window, we drop down to Y coordinate 51 and eventually get down to the right edge at Y coordinate 59. 

Routines that draw objects make use of these commands - we set a window where we want to draw an object and then stream in the number of pixels needed to fill that window. They can be pixels of a common value, in order to clear an area to a uniform color. They can be uniform color pixels to draw a rectangle or square inside a larger area. They can be areas where patterns from the pixels form arcs, circles, lines and even font characters. 

SCREEN OPERATION COMMANDS

There are commands to put the controller to sleep (power conserving mode) and bring it out of sleep. Commands turn on the display or turn it off. Commands can flip on all pixels or flip off all pixels. Commands can set the display into inverse mode - i.e a font character can look like black ink on white paper or like white phosphors on a black (or green) screen. 

I will be experimenting with these commands as they are simple sequences that should produce visible results on the monitor to quickly confirm my understanding. That will take place next. 


Drawing on the LCD screen to test functions I need - part 5

BATTLING THROUGH TO SPI SIGNAL OUTPUTS

After some more changes, such as removing the device from the device tree entry for spim1, I decided to revert back to using the FPGA routing for spim0 since that had some effects initially. This time I routed the signals out of the GPIO pins directly without buffers, to avoid the timing problem I suspected would occur because of the buffers I had been inserting.

I had also created a diagnostic monitoring function to show the state of all the relevant SPI registers. This showed me that I had the SPI capability properly configured and the status was good. I then hooked up the scope to the new 

The loop I wrote showed me good activity on the clock, MOSI, MISO and hardware slave select lines. I hooked up the LCD Module but still had nothing visible. I did notice that the SPI link is operating at 5MHz rather than the 2.5MHz rate I thought I was configuring. I corrected the constants in my initialization routine and corrected this minor flaw. 

NEXT STEP - ENSURE OUR BIT ORDER AND OTHER FACTORS ARE CORRECT

What I saw on the scope confirmed that the bits on MOSI changed at the correct clock phase, that the clock polarity is correct and that the bits I see are in fact the data I am transmitting. The most significant bit is transmitted first in every byte. Since the library and my code handle the two bytes of a 16 bit word explicitly there is no issue with the little-endian nature of the ARM processor as I send the high byte first. 

CHECKING THE FPGA GENERATED SIGNALS THAT ARE PART OF THE COMMUNICATIONS

I produce four signals that are important for communicating with the LCD controller. LCD Reset is used to produce the desired initial state when we start up the program. LCD Command/Data tells the controller chip whether we are transmitting an 8 bit command or 16 bit data/parameter words. LCD Select is used to enable the logic to receive our SPI communications and transfer it in parallel to the controller chip. Touch Select is used to instead enable communications with the touch screen controller chip. 

These all work as intended. I can see a transaction bounded by my LCD Select signal, with the actual SPI link sending 8 bits at a time bounded by the hardware slave select. My LCD Select governs however to comply with the wacky protocol this device uses. 

I send MSB first so a xC2 goes out as 1 1 0 0 0 0 1 0 and these get shifted into the first SIPO shift register such that D7 is a 1 and D0 is a 0. The LCD controller chip is wired so that our data is appearing in the lower half of its 16 bit parallel input with D0 being the least significant bit. The LCD controller defines commands such that the 0xC2, Power Control 3, is represented with a 1 at D7 and a 0 at D0 which again confirms we are transmitting the bytes exactly as the controller chip wants to see them. 

As I deassert my LCD Select signal after the transmission of the one byte 0xC2, this triggers the LCD controller chip to latch this in and read it as a command, while resetting the logic outside to receive subsequent bytes or words. Again everything appears correct to be communicating with the LCD controller and sending the proper sequence to initialize it

TEST PROGRAM COMMANDS AND RESPONSE

I did discover some logical issues concerning what I need to do in order to use the LCD library provided by Waveshare which I have heavily modified in order to port it to the DE10-Nano. When I try to clear the screen to a fixed color I selected, nothing was transmitted over SPI. After instrumenting the library routines, I see that it does a check on the input parameters and skips clearing because the start and end points are all the same. 

It appears that I don't have some important initialization done for the screen data structure as it has the end points of the X and Y range as 0 instead of their correct 479 and 319 for the full extend of the screen. I will chase down where this should have been set and make sure that happens. 

Similarly, my tests writing fixed strings on the screen are failing because of this issue with screen data structure initialization. I may also need to issue some commands to set the current cursor or writing point before I start - this is an area I don't fully understand. 

Saturday, June 10, 2023

Drawing on the LCD screen to test functions I need - part 4

SCOPE REVIEW OF SIGNAL CORRECTNESS

There was a bit of delay working out how to get the scope probes on the jumpers, since both connectors where the jumpers plug in are tightly spaced. Plugging in my 2x7 cable for .5mm pins at 2mm spacing, I yanked the other connector off and gained access to the wire ends. 

Pin 4 is the SPI clock signal, pin 7 is the SPI MOSI signal and pin 5 accepts the SPI MISO signal. SPI Slave Select sits on pin 6 and will be useful for triggering although the actual slave select going to the LCD module comes from the FPGA side GPIO pins and not the SPI modules slave select logic. I had to do this to deal with the kludge protocol required to drive this LCD Module. 

I saw no signal activity at all. Nothing. The SPI module is not working or not being routed as it should out through the LTC pins. My program was supposedly looping writing command bytes to the module which should have toggled slave select, drove the clock, and output the bit pattern I wrote. 

DEBUGGING STEPS

My first thought was that it was the damnable poorly documented routing mechanisms for the Cyclone V and the Quartus toolchain. I went back to the tool and opened the pin planner. To my surprise, I found that the SPI signals were NOT connected to the Cyclone V chip in spite of the Platform Designer and Quartus statements directing this to happen.

I look at the table and noticed that the pins listed for the SPI master in pin planner were different from the table in the user manual. I then opened my master spreadsheet of all the signals and routing for the Cyclone V chip to dig into it. To my surprise, I discovered that the SPI master connected to the LTC connector was not SPIM0 as I expected and configured - the primary SPI controller - but SPIM1 a second controller. 

As you can see, neither the table nor the diagram that sits above it has bothered to distinguish which of the two SPI masters is involved. The signal name HPS_SPIM_MOSI is not a real signal name, there are instead HPS_SPIM0_MOSI and HPS_SPIM1_MOSI and had some lazy so and so bothered to type the number I would have saved quite a bit of time digging into this. 

This is why I had some activity when my earlier design had routed the SPIM0 signals out to the FPGA and to GPIO pins. That method required me to introduce IO Buffer blocks which introduced delays in the signals, which was the impetus for me to switch over to the LTC connections instead.

I can change routing to get the SPIM1 routed to the LTC pins, replacing SPIM0. I would also need to change the offset I use to access the SPI control registers in memory, as SPIM1 is mapped to a different range of addresses. 

The remaining potential issue is that there is a Linux driver enabled for SPIM1 according to the device tree blob file. I used the dtc compiler to access this, vi to change spim1 to disabled, the compiler to format as a blob and was able get Linux to boot properly.

RESULTS OF SWITCH TO THE SPIM1 CONTROLLER

Still no signals showing up on the oscilloscope while the program is looping writing to the SPI channel. I realize that the device tree had listed a specific peripheral using SPI as attached to SPIM1 which may cause the driver to still interfere. My next change will be removal of this device mention in the device tree. 

For diagnosis, I began to litter the code with diagnostic printf statements so that I can validate every address being used. I also accessed and displayed lots of values to demonstrate that I have done what I expected to accomplish. 

Musings on the LCD Module I am using - missing capability

WAVESHARE PICO RESTOUCH LCD 3.5

This is the module I bought https://www.waveshare.com/wiki/Pico-ResTouch-LCD-3.5#Setup_environment and it is serving my needs adequately although it is missing the ability to read anything from the LCD controller chip which would have been handy in debugging and experimentation.

HILLBILLY STYLE SERIAL INTERFACE GRAFTED TO THE LCD CONTROLLER

As I have mentioned in some detail before, this particular module has introduced an odd duck serial interface, not quite SPI, not I2C, but one that can be driven by (some) SPI implementations on microcontrollers and microprocessors. The LCD controller supports serial interfaces but the designer of this module chose to configure it in 16 bit parallel mode, then convert incoming serial to parallel with a hodgepodge of TTL chips. 

Anyone think I have used enough pejoratives describing this? Hodgepodge. Hillbilly. Odd Duck. Oddball.  Previously I did characterize it as Rube Goldberg-ish but really it is not that sophisticated an kludge. Enough already!

This clocks in serial data from the SPI MOSI line into a 16 bit latching SIPO shift register. It is able to handle frame (word) sizes of both 8 bit and 16 bit. It introduces rules for the SPI Slave Select line that help drive the LCD controller chip but are outside of SPI protocols. 

Most seriously, since it is not really SPI, it has no means of sending data back from the LCD controller to the host over the SPI MISO line. This is unfortunate because the LCD controller has a rich set of commands that return internal state, configuration information and memory contents, all of which are unavailable. 

The parallel interface of the controller chip would place the outgoing data on the 16 signal lines, but there are no TTL chips or wiring to receive them. The existing shift register could make use of its output enable pin to stop driving the parallel outputs, thus allowing the tristate LCD controller signals to output during a read. 

Read and write operations on the 16 parallel pins of the LCD controller are effected by rising edge signals on the write enable (WRX) and read enable (RDX) lines. The TTL assemblage that is converting serial to parallel does toggle the WRX line to cause it to capture the data or commands coming from the host. The RDX line is hard wired to 3.3V and thus never has an edge to drive a read output. 

POSSIBLE ENHANCEMENT TO SUPPORT READING FROM THE LCD CONTROLLER

One could imagine that additional circuitry could be added with a second board and the RDX line redirected from 3.3V to this new circuitry. The challenge is that the parallel interface and the command structure of the LCD controller would require a sophisticated state machine to determine when to strobe the RDX line, based on what commands had been transmitted and when those would be returning data. 

For example, the command 0x09 will read out the status of the LCD controller and screen. One would write the 8 bit command to the device, then write up to four 16 bit words whose value doesn't matter because we are capturing the returning data from the MISO line. 



The nature of SPI is that we are simultaneously sending the bits from host to controller (MOSI) and from controller to host (MISO), like ships passing in the night. Thus one cannot respond over MISO to something that is coming over MOSI since you will not have seen the MOSI data yet, all responses need to be sent over subsequent transfers. This command sends x09 and ignores MISO, then sends four more irrelevant transfers over MOSI while watching for returns from MISO.

A peculiarity of the controller chip means that for many read type commands the data is not yet ready to return when the second transfer begins (the first data transfer after the command is sent). Thus we are essentially doing a dummy exchange of MOSI and MISO as the first data transfer. The second and subsequent data transfers is when the MISO information is being returned. 

My state machine would need to read every command sent, determine what the pattern of returned data will be, then block the WRX on the data transfers and instead toggle RDX to latch in the returning data. If done properly, a second (PISO) shift register could clock out that data on the MISO line fulfilling the expected behavior. 

Note that the 8 bit command and values are part of the 16 bit parallel interface, we just ignore the high order 8 bits. The wacky protocol they devised transmits a short 8 bit word rather than a 16 bit word with zeroes at the top, when sending commands, but sends the full 16 bits when transmitting data. 

This is far too much effort for little practical benefit. I will not be attempting it, but I burned the brain timing musing over it and decided to share it just in case this helps someone with a similar design issue. 

Thursday, June 8, 2023

Debugging the small BMP display routine - used for up and down arrows

DRIVER CODE SET UP TO INTERCEPT LCD LIBRARY CALLS

I don't yet have the SPI link to the actual LCD Module debugged, but I built a mainline routine that would expose the same library calls as exist in the actual library, so that my code to display the BMP could execute. I would intercept each library call and show diagnostic information about that invocation. 

The three call types that are used by my code are LCD_Clear, LCD_SetWindow and LCD_WriteData which set the display to a uniform background, establish the size of the window within the screen for our output, and outputs each pixel. The scan direction is set up so that pixels are placed Left to Right in a line, then Up to Down for successive lines - L2R_U2D mode in the library. 

In an early version of the program I output the 16 bit hex value for each pixel - our display module uses a cut down color space with 5 blue, 6 green and 5 red bits squeezed into a 16 bit value. This chewed up a lot of space on the terminal with almost 17,000 lines output to represent the bitmap image. 

INITIAL PROBLEM WITH COLOR DEPTH

My code rejected the initial BMP file because it was not a 24 bit color depth. I realized that the MS Paint application, which offers only the options of 'black and white' or 'color', was generating 32 bit color depth. I found an online converter that would make the bitmap 24 bit, repaired my files and was then able to work with them using my code. 

SUCCESSFUL TEST OF THE CODE

My code displayed the calls to narrow down the window for the arrow to a specific 130 x 130 area starting at x value 300 and y value 40, representative of what my user interface will do. It then counts each time we enter with a 16 bit word that sets the color of one pixel in the window. At the end we see that the count was 16,900 and we called to reset the drawing window back to the entire 480 x 320 screen area. 

I may have an issue if the bitmap file is ordered from bottom row to top or some other way compared to my L2R_U2D orientation, but if that is so I can either modify the bitmap or call the library routine to switch the screen mode to whatever is appropriate. This will have to wait until I see the bitmap on the actual LCD screen.