3d Printing: Display Part 1 & Project Name

I contacted Cypress/Infineon, and they do *NOT* want the term PSoC ™ used in conjunction with a product. I suppose there is some trademark law involved also. The interesting thing was their list of trademarked items sent back to me did not include PSoC. I will always use PSOC in discussions, since that does not include the little “o”, which, if I understand trademarks properly, is required as the trademarks require the little nuances to be valid.

Regardless, it changed my plans a little. I was going to prominently feature the PSOC as part of the identification of the 3d printer project I am working on. No more. The term SOC will be used instead. In order to be reminiscent of, but not related to, I’ve decided to call the 3d printer adapter board project the SOCino project.

The SOCino will provide a carrier that is more or less pin compatible to the Arduino board so you can plug in a RAMPS board and drive a 3d printer with it. The SOCino board holds a CY8CKit-059 board that you can purchase from Cypress/Infineon or Mouser or Digikey. There are a few reasons for a carrier board rather than a new circuit board like the FreeSoc2.

First, it is obtainable by the weekend engineer. You get a debugger, processor, and I/O brought out on 0.1″ centers (25.4mm) that you can plug into breadboards or place on a carrier for $10 to $15. Ultra cheap. The carrier board is just compatible enough (on purpose) with RAMPS to print and drive a serial interface display board.

Second, using Other People’s Money, in terms of boards created that demonstrably work and are cheap to purchase. With the carrier board, I have just hosted a RAMPS 1.4 and a smart controller adapter board.

Finally, with pre-tested, working hardware you can eventually succeed. I am now able to put text onto a RepRapDiscount Full Graphic Smart Controller display. The display board kit was purchased off of Amazon for around $15. You can get it cheaper elsewhere.

The journey to this was not easy. It took over a month of part time work and much hair pulling. At my age, the hair is leaving faster than the pulling. The result is I am now starting to resemble a shaggy cue-ball. But I digress. Onward to the circus!

Big Problem: Information Quality

The problem with controlling even the RepRap Full Graphic Smart Controller (RRDSFGC) is the vagueness of the information on the internet. I have designed and implemented real time firmware since 1978, and I am used to having fairly easy to read schematics and correct documentation.

Obtaining either of those items for the displays which are able to work serially on the smart controller adapter board is difficult. In addition, there are often obvious and glaring errors in the documentation you do acquire.

Obvious Errors: Original

To prove my point, look at the only schematic style image I could find for the smart adapter board. (I could find gerber files, but no layout files, Eagle or otherwise):

Incorrect Schematic Information (especially for Smart Full Graphic Controller)

The first item that caught my eye was the fact there are two “D49” emblems. It took me a while to wrap my head around the RAMPS board and the designations, but it is obvious that information in the display is wrong. Since I was not dealing with that connector as of yet, I ignored it.

So, I did a ton of research. One of the most helpful sites I visited was the instructables site here . That lead me to look up the ST7920 device. I finally found a good data sheet for it, with a connector schematic. I have uploaded it here. (Warning: This is a zipped PDF. I zipped it myself. I opened the pdf on Mac. Have not tested on Windows. PDF’s can contain bad stuff.)

First, I found the U8G2 library. That thing is huge! My first attempt at using it on the PSOC 5 on a breadboard almost worked, but I did not understand a lot of things. (I had not yet found the data sheet(s).) I eventually abandoned the U8G2 library. It kept me too isolated from the real problems.

I finally ordered the kit previously mentioned, which includes cables and smart adapter board, because I did not have that adapter board. I then started looking for the schematic for the smart adapter board. I could not find it. I did find a .png with some information, but that information turned out to also be wrong. It followed, more or less, the same information in the first image previously shown, but at that time I was clueless.

Once I *thought* I had the right information (based on the image previously shown), I was ready. I read the information on the instructables site, and started coding and designing circuitry. I was interested in how to play with the display, not the code the user created.

That circuit designing and coding took several days. When finished, I hooked it up and nothing worked. I am insane, so I kept doing the same things over and over again. I re-examined my stuff. The information on the internet is always right! (NOT)

I finally realized I had not created a “D23” connection on my carrier, so I did so using a jumper wire. (1V3 now in the works) Then, I assigned pins per the diagram above, along with other information given on various other sites. I had not yet read the data sheet. (Big mistake)

Nothing worked. Hmm. Go back to information. Go back and (finally!) start reading the data sheet. Hmm. Bad setup for the commands. Fix. Test. Nothing. Re-read, re-write. Test. Nothing.

The definition of insanity is to try the same things over and over and expect different results. I needed to change something. So I went back to square one. I got my ohm-meter out. I read the data sheet (finally!). I measured and verified connections. I found other sites with information on the display.

I finally found a version of the data sheet with a schematic with the pin out on the bottom of the display. Hurrah! That schematic matched 2 out of 3 of the websites! Good!

I started ohming (technical term known to all techs, see if the wires are connected using an ohm-meter) the connection to the display through the connector on the RAMPS board to D16, D17, and D23 connections. Hmm! Different from the documentation!

Using the data sheet for the ST7920 data module, I correctly identified the signal paths to the RRDSFGC display module. Hmm. Clock (E) and Data (R/W) and RS/CS are swapped! I also determined the correct “Dxx” lines for the bottom part of the board. Here is the corrected “schematic” image:

Corrected Smart Adapter “Schematic” image

Note that the pins D16, D17, and D23 have different uses that what you usually see described for the smart adapter connected to a RepRap controller. I suspect other controllers use different pins, causing bad information to be propagated.

Finally! Working Display. The Details:

I want to (eventually) be able to use the SPI port for other things at the same time I am controlling the display. So I set up to select the display properly. Then, after reading the data sheet on the SPI controller component in the PSOC Creator library, I realized the select line would stay high for the entire transaction if the entire SPI transmit buffer was filled with the outgoing data (and not allowed to be empty between data bytes out).

The design I arrived at is:

SPI Display Interface

The Configuration for the SPI Master controller is:

Configuration for the Display SPI interface

I am not using interrupts on this SPI as of yet, so this is the only configuration that needs to be made. It is essentially the default for this component. The ISR component is for future use, if needed.

Hardware Usage

To use the display, set the crDisplayEnable register to a value of 1. That disables the normal “SS” signal which defaults to low, and enables the Display Select signal which is required to be high. This inverting of typical usage was easy to do with the ability to add logic in the schematic of the PSOC Creator.

Initial Code

I will include most of my “control” code in the next post. For this post, I will give you the layout of the code, and enough code snippets for you to experiment with the display, and some bottled genius. (99% perspiration.)

At the beginning you have to initialize the SPI, and set up the selection line to make sure the Display is being controlled.

I am wrapping the code in an #if so that it can be removed by changing a header file. Fairly simple code here:

#if ENABLE_DISPLAY
//-------------------------------------------------------------
// enable hardware dealing with display
//-------------------------------------------------------------
void initializeDisplayHardware() {
    
    crDisplayEnable_Write(1);//make sure RS is selected
    SPI_Master_Start();
    CyDelay(10);//wait for initialization to finish (if needed)
    SPI_Master_ReadTxStatus();// clear leftover interrupts, etc
}
#endif

Sending To The Display Controller

If the SPI transmit buffer (default 4 bytes max) is loaded before the first byte is sent completely, the SS line stays selected. In “normal” code, you would disable global interrupts and re-enable global interrupts. When dealing with FreeRTOS, there are a couple of macro functions that do this for you. So, the code to write command or data to the is fairly simple.

Note that the default method for talking to the display is to send 3 bytes. The first byte is a SYNC along with a Select and a Data/Instruction flag followed by two four bit nibbles that comprise an 8 bit byte. Rather cumbersome, but it is built on how the parallel interface works in 4 bit mode.

The code to send the 3 bytes out, which are hosted in the “messageToST7920” array is as follows. Note that almost all functions are declared static so the function names can be reused in other modules without conflict:

#define ST7920_NUMBER_OF_BYTES_IN_COMMAND_OR_DATA 3
//-------------------------------------------------------------
// Send the data byte to the display. 
//  NOTE: sync and command are previously placed in 
//    messageToST7920 array at index 0
//  always assume 4 bit serial interface.
//-------------------------------------------------------------
static void sendToDisplay(uint8 data){
    messageToST7920[1] = data &0xf0;// supposedly lower bits 
                                    //  can be non-zero
    messageToST7920[2] = data << 4;    

    taskENTER_CRITICAL();// shut down all interrupts
    // we can't be swapped out while writing to the
    // SPI so the message will go out with 1 address
    // select.
    for (int16 i=0; 
         i <  ST7920_NUMBER_OF_BYTES_IN_COMMAND_OR_DATA; i++){
        SPI_Master_WriteByte(messageToST7920[i]);
    }
    taskEXIT_CRITICAL();
    
    // wait until display receives the command,
    // then delay 72 microseconds (per the data sheet)
    while (!(SPI_Master_ReadTxStatus() & SPI_Master_STS_SPI_DONE));// loop until display rcvs command
    
    CyDelayUs(72);// wait over 72us for command to 
                  //process per ST7920 data sheet
}

The following code sets/reset the extended bit in the controller: (see the data sheet!)

    #define ST7920_SERIAL_SYNC_BITS             (0b11111000)
    #define ST7920_SERIAL_RW_BIT                (0b00000100)    // DB2 RW high == read (read not avail serial)
    #define ST7920_SERIAL_RS_BIT                (0b00000010)    // DB1 RS high == data (low == instruction (register))    
    #define ST7920_EXTENDED_FIXED_BIT           (0b00100000)    // DB5
    #define ST7920_EXTENDED_RE_BIT              (0b00000100)            // RE=1 == extended instructions
    #define ST7920_ENABLE_GRAPHICS_BIT          (0b00100000)

//-------------------------------------------------------------
// set/reset the extended function bit in the display
//-------------------------------------------------------------
static void setExtendedFunction(int16 extended, 
                               int16 graphicsEnable) {
    uint8 command = ST7920_EXTENDED_FIXED_BIT;// preload
    // write is a 0
    messageToST7920[0] = ST7920_SERIAL_SYNC_BITS // sync
                        & ~ST7920_SERIAL_RW_BIT  // write
                        & ~ST7920_SERIAL_RS_BIT ;// instruction
    
    if (extended)
        command |= ST7920_EXTENDED_RE_BIT;
    
    if (graphicsEnable)
        command |= ST7920_ENABLE_GRAPHICS_BIT;
    
    sendToDisplay(command);   
}

The following code turns the display on, and changes mode:

    #define ST7920_BASIC_INSTRUCTION_BITS       (0)
    #define ST7920_DISPLAY_CONTROL_BIT          (0b00001000)    // DB3 1== display control
    #define ST7920_ENABLE_DISPLAY_ON_BIT        (0b00000100)    // DB2 1== display on

    #define ST7920_ENABLE_CURSOR_ON_BIT         (0b00000010)    // DB1 1== cursor on
    #define ST7920_ENABLE_BLINK_ON_BIT          (0b00000001)    // DB0 1== blink on

//-------------------------------------------------------------
// turn display on/off, change cursor settings
//-------------------------------------------------------------
static void setDisplayStatus(uint16 ON,
                        uint16 showCursor,uint16 blinkCursor) {
    uint8 command = ST7920_BASIC_INSTRUCTION_BITS | 
                    ST7920_DISPLAY_CONTROL_BIT;// preload basic

    if (ON)
        command |= ST7920_ENABLE_DISPLAY_ON_BIT;

    if (showCursor)
        command |= ST7920_ENABLE_CURSOR_ON_BIT;
    
    if (blinkCursor)
        command |= ST7920_ENABLE_BLINK_ON_BIT;
    
    sendToDisplay(command);
}

Finally, we initialize the display when the processor comes up. We have to wait a certain amount of time for the controller to initialize, per the data sheet, so we do a TaskDelay, even if it may be redundant due to other delays in the system. Note this code has been sanitized to remove references to a profiler and other things that are not important in this discussion.

//-------------------------------------------------------------
// Pattern to initialize the display, from the sitronix7920 
//  data sheet page 34
//-------------------------------------------------------------
void initializeDisplay() {
    
    TaskDelay(pdMS_TO_TICKS(50));// wait for display to power up initialize

    // do the set function for the extended bit
    setExtendedFunction(0 /* not extended*/,
                        0 /* no graphics*/);

    // do the set function the second time (first time keyed it to be Instruction set 0)
    setExtendedFunction(0 /* not extended*/,
                        0 /* no graphics*/);
    // now turn display on
    setDisplayStatus(1 /* display on*/, 
                     0 /*dont show cursor*/ , 
                     0 /* dont blink cursor */); 
    // now clear the display
    clearDisplay();// delays at least 1.6 ms (in our case, 3 is used)
}

The clear display function is as follows:

    #define ST7920_CLEAR_DISPLAY_COMMAND        (0b00000001)    // DB0 1== clear ram

//-------------------------------------------------------------
// clear the display.  Requires a wait of at least 1.6ms
// ST7920 data sheet page 35 states wait 10 milliseconds
// do TaskDelay() for the wait
//-------------------------------------------------------------
static void clearDisplay() {
     
    messageToST7920[0] = ST7920_SERIAL_SYNC_BITS // sync
                        & ~ST7920_SERIAL_RW_BIT  // write
                        & ~ST7920_SERIAL_RS_BIT ;// instruction 
   
    
    sendToDisplay(ST7920_CLEAR_DISPLAY_COMMAND);
    
    TaskDelay(3);// datasheet indicates 1.6ms.
}

Next Time

Next Time I hope to be able to give the full files and the task used to test the display. The full project will be available at the end of the 3d printer series. Here is the result of the test:

Add a Comment

Your email address will not be published. Required fields are marked *