Matrix Orbital GTT43A: Driver Library – Part 2

Summary

In the previous article I showed you how to integrate the Matrix Orbital Driver into a PSoC4200M project.  I am planning on using this device on a bus with multiple displays, and using an RTOS.  The byte based driver in the previous example isn’t that great for this situation.  In one of the earlier articles I showed you how to build a packet based interface instead of a byte-based interface.  Lets integrate that into the PSoC4200M project, and add some more commands.

In this article I will

  1. Integrate the packet driver
  2. Add event handlers
  3. Add some new commands
  4. Fix a nasty little bug that is lurking in the driver

Integrate the Packet Driver

In the file gtt_parser.c there is a big long function which reads a byte at a type every time that it is called.  It then assembles the packet into a buffer of bytes that the rest of the system can consume.  After the packet is completely read, it sets up pointers to the start and end of the packet and finally calls the function “gtt_process_packet”.  For me what I will do is read in a packet, then call this function to setup things and call the gtt_process_packet.

// This function process a whole packet at a time    
uint8_t gtt_parser_process(gtt_device *device)
{
    gtt_packet_error_t rval;
    rval = device->ReadPacket(device);
    if(rval == GTT_PACKET_NODATA)
        return 0;
    
    if(rval != GTT_PACKET_OK)
    {
#if DEBUG_PSOC        
        sprintf(buff,"GTT_PACKET_ERROR %d\r\n",rval);
        UART_UartPutString(buff);
#endif
        return 0; // No data
    }

    device->Parser.PacketStart = device->Parser.Index;
    device->Parser.Index += device->Parser.Length;
    
	uint8_t Result = gtt_process_packet(device, device->Parser.PacketStart);
	if (Result)
	    return 0;
	else
	    return 1;

}

I use almost the same packet driver as I built in the earlier example.  Except that I need to modify it to read into the gtt buffers that the gtt driver library expects.  The biggest benefit of this whole thing is that it makes complete I2C transactions, rather than issuing a bunch of start/address/reads which makes it significantly more efficient.

gtt_packet_error_t readPacketI2C(gtt_device *device)
{
    
    uint8_t data;
    uint32_t i2cerror;
    
    i2cerror = I2C_I2CMasterSendStart( ((i2cContext_t *)device->Context)->slaveAddress,I2C_I2C_READ_XFER_MODE , ((i2cContext_t *)device->Context)->timeout);
    i2cerror |= I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA,&data,((i2cContext_t *)device->Context)->timeout);
    i2cerror |= I2C_I2CMasterSendStop(((i2cContext_t *)device->Context)->timeout);
    
    
    // Something bad happened on the I2C Bus ....
    if(i2cerror)
    {
        sprintf(buff,"I2C Return Code %X\r\n",(unsigned int)i2cerror);
        UART_UartPutString(buff);
        return GTT_PACKET_I2CERROR;
    }
    
     // The screen returns a 0 when there is nothing in the buffer.
    if(data == 0)
    {
        return GTT_PACKET_NODATA;
    }

    // This is bad because there was something other than a packet start byte
    if(data != 252)
    {
        sprintf(buff,"bad data = %d\r\n",data);
        UART_UartPutString(buff);
        return GTT_PACKET_DATABAD;
    }
    
    // We know that we have a command
    i2cerror = I2C_I2CMasterSendStart( ((i2cContext_t *)device->Context)->slaveAddress,I2C_I2C_READ_XFER_MODE , ((i2cContext_t *)device->Context)->timeout);
    i2cerror |= I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA,&data,((i2cContext_t *)device->Context)->timeout); // command
    device->Parser.Command = data;

    // Read the Length
    i2cerror |= I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA,&data,((i2cContext_t *)device->Context)->timeout); // length
    device->Parser.Length = data<<8;
    i2cerror |= I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA,&data,((i2cContext_t *)device->Context)->timeout); // length
    device->Parser.Length += data;
    i2cerror |= I2C_I2CMasterSendStop(((i2cContext_t *)device->Context)->timeout);
    
    if(i2cerror)
        return GTT_PACKET_I2CERROR;
    
    if(device->Parser.Length > device->rx_buffer_size)
    {
        return GTT_PACKET_SIZE;
    }
    
    // If the packet has any data... then read it.
    if(device->Parser.Length != 0)
    {
        i2cerror |= I2C_I2CMasterSendStart( ((i2cContext_t *)device->Context)->slaveAddress,I2C_I2C_READ_XFER_MODE , ((i2cContext_t *)device->Context)->timeout);
    
        for(uint32_t i=0;i < device->Parser.Length-1; i++)
        {
            i2cerror |= I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA,&data,((i2cContext_t *)device->Context)->timeout); // length
            device->rx_buffer[device->Parser.Index+i] = data;
        }

        // Read the last byte
        i2cerror |= I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA,&data,((i2cContext_t *)device->Context)->timeout); // length
        device->rx_buffer[device->Parser.Index +device->Parser.Length - 1 ] = data;
        i2cerror |= I2C_I2CMasterSendStop(((i2cContext_t *)device->Context)->timeout);
        
        if(i2cerror)
            return GTT_PACKET_I2CERROR;
    }
      
    sprintf(buff,"command = %d length = %d bytes= ",device->Parser.Command,device->Parser.Length);
    UART_UartPutString(buff);
    for(uint32_t i=0;i<device->Parser.Length;i++)
    {
        //sprintf(buff,"%d ",inbuff[i]);
        sprintf(buff,"%d ",device->rx_buffer[device->Parser.Index+i]);
        UART_UartPutString(buff);
    }
    UART_UartPutString("\r\n");
    return GTT_PACKET_OK;
}

Add Event Handlers

I noticed when I looked at the gtt_device.h that the structure for the gtt_device has an member called “gtt_events”, but what is that?

typedef struct gtt_device
{
	void* Context;            /* device depended storage */
	gtt_write Write;          /* Function for writing data */
	gtt_read Read;            /* Function for reading data */
    gtt_packet_error_t (*ReadPacket)(gtt_device *);

	uint8_t secured_packets;  /* 0 = regular protocol, 1 = wrap all outgoing packets with crc protection*/
	
	/* The fields below are internal and shall NOT be used by the read/write functions */
	
	gtt_parser Parser;        /* Protocol parser data */
	uint8_t *rx_buffer;       /* Buffer for incoming data */
	size_t rx_buffer_size;    /* size of the rx buffer in elements */
	uint8_t *tx_buffer;       /* Buffer for outgoing data */
	size_t tx_buffer_size;    /* size of the tx buffer in elements */
	size_t tx_index;          /* current index for the packet writer */
	gtt_events events;        /* Event Callbacks */
	size_t wait_idx;          /* Current Packet Index for the waitlist */
	gtt_waitlist_item waitlist[8]; /* Packet recieve waitlists */
} gtt_device;

Well. the gtt_events structure is defined in gtt_events.h.  Basically it is a bunch of function pointers, which if you provide functions, it will call those functions when things happen on the screen.  For instance the function that gtt_event_slider_change is pointing to will be called when a slider changes.

typedef void(*gtt_event_key)(gtt_device* device, uint8_t key, eKeypadRepeatMode type);
typedef void(*gtt_event_sliderchange)(gtt_device* device, eTouchReportingType type, uint8_t slider, int16_t value);
typedef void(*gtt_event_touch)(gtt_device* device, eTouchReportingType type, uint16_t x , uint16_t y);
typedef void(*gtt_event_regiontouch)(gtt_device* device, eTouchReportingType type, uint8_t region);
typedef void(*gtt_event_baseobject_on_property_change)(gtt_device* device, uint16_t ObjectID, uint16_t PropertyID);
typedef void(*gtt_event_visualobject_on_key)(gtt_device* device, uint16_t ObjectID, uint8_t Row, uint8_t Col, uint8_t ScanCode, uint8_t Down);
typedef void(*gtt_event_button_click)(gtt_device* device, uint16_t ObjectID, uint8_t State);

typedef struct gtt_events {
	gtt_event_key key;
	gtt_event_sliderchange sliderchange;
	gtt_event_touch touch;
	gtt_event_regiontouch regiontouch;
	gtt_event_baseobject_on_property_change baseobject_on_property_change;
	gtt_event_visualobject_on_key visualobject_on_key;
	gtt_event_button_click button_click;
} gtt_events;

To start with I just created stub functions that would just print out the information.  Here is an example of a function for the “gtt_event_button_click”

void my_gtt_event_button_click(gtt_device* device, uint16_t ObjectID, uint8_t State)
{
    (void)device;
    (void)ObjectID;
    (void)State;
    UART_UartPutString("event button click\r\n");
}

Once you have those functions you need to add them to the gtt_device structure like this:

gtt_events myEvents = {
    .sliderchange = my_gtt_event_sliderchange,
    .touch = my_gtt_event_touch,
    .regiontouch = my_gtt_event_regiontouch,
    .baseobject_on_property_change = my_gtt_event_baseobject_on_property_change,
    .visualobject_on_key = my_gtt_event_visualobject_on_key,
    .button_click = my_gtt_event_button_click
};

Add New Commands

Now we are ready to update the test project to add some more commands.  Here are a few examples which call the “gtt25” functions.

            case 'q':
                UART_UartPutString("Set Text\r\n");
                gtt25_set_label_text(gtt,2,t);
            break;
                       
            case '2':
                gtt25_set_slider_value(gtt,3,2);
            break;
               
            case '9':
                gtt25_set_slider_value(gtt,3,9);
            break;
                    
            case 'I':
                gtt_set_default_channel(gtt, eChannel_I2C);
            break;
                    
            case '+':
                count += 1;
                if(count>100)
                    count = 100;
                gtt25_set_gauge_value(gtt,9,count);
            break;    
                
            case '-':
                if(count > 0)
                    count -= 1;
                gtt25_set_gauge_value(gtt,9,count);
            break;

Fix a Nasty Little Bug

While I was debugging the library I found myself where the program was hung.  When I ran the debugger I found myself here.  This means that there was an ARM exception.  But why?

Then when you look at the call stack you find out that the exception is in the function “gtt_parser_getS16”

OK… but what in the world?  All this function is doing is taking the bytes and casting them into a uint16_t

Well it turns out that if the address that is being read is ODD meaning not even aligned, you will endup with an ARM exception for an unaligned access of the memory.  This is why you need to be super careful with a pointer cast.  In this case you are casting a uint8_t pointer which can be byte aligned.

Here is a proper fix to this problem, assemble the composite type byte-by-byte.

int16_t gtt_parser_getS16(gtt_device* device, size_t index, size_t *outIndex)
{
    
    int16_t data = (device->rx_buffer[index]<<8 | device->rx_buffer[index+1]);	    
	*outIndex = index + 2;
    return data;
  
}

In the next article I will port all of this stuff to PSoC 6.

You can "git" these projects from

https://github.com/iotexpert/GTT43A

And the driver library from 

https://github.com/iotexpert/GTT-Client-Library

Title
Matrix Orbital GTT43: A Cool Display
Matrix Orbital GTT43A: Serial Interface
Matrix Orbital GTT43A: GTT Scripts
Matrix Orbital GTT43A: A PSoC 4 Interface
Matrix Orbital GTT43A: Debugging the I2C
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: PSoC 6 using RTOS and the GTT Driver Library

Matrix Orbital GTT43A: Driver Library – Part 1

Summary

In this article Im going to show you how to build a driver for the Matrix Orbital GTT43A that I have been talking about in the last several articles.  As you can see from the protocol manual, the Matrix Orbital Display has a bunch of different commands.  And I know that I need a driver.  But there isn’t (yet) one on the Matrix Orbital Website.  However, when I look at the pre-release of the GTT25 protocol guide, it seems clear that they are planning on one.  But what to do in the interim?  As usual Google is your friend and after looking around a little bit I found this YouTube video of a Matrix Orbital Demo.  One more Google search lead me to this Matrix Orbital GitHub repo which appears to hold the driver that they wrote for this demo.

Clone it! Clone it! Good.

Now what?  In this article Ill show you:

  1. How to port the library to PSoC
  2. How to implement the HAL as conceived by Matrix Orbital
  3. How to make a test jig

And in the next Article I will

  1. Replace the HAL and Parser with a Packet based HAL, much better
  2. Show you how to fix some bugs in the library (nastiness)
  3. Add more test code

And in the one after that Ill port the whole thing to PSoC6 & RTOS

Port the GTT Client Library

After running “git@github.com:MatrixOrbital/GTT-Arduino-Thermometer-Demo.git” I look around a little bit… and immediately find “GttClient”.  And when you look there, perfect a bunch of C and Header files.

The first step is to make a new PSoC Creator project.  As I have done in the past, Ill drive the display with I2C, and Ill build a command line parser to talk to the system.  Here is the schematic:

Assign the Pins (I am using a PSoC 4200M, my favorite PSoC, development kit, specifically the CY8CKIT-044)

After running “Generate Application”, the next thing I do is pull in the library into PSoC Creator.  To do this

  1. Right click on the Source Files and make a new Folder, rename it “GTT”
  2. Right click on the GTT Folder and do “Add Existing Item…”
  3. Navigate to the GttClient directory, select all of the .h and .c files

Your WorkSpace Explorer should look like this now:

Because those files are not in the normal build path, you next need to add the directory to the include path so that PSoC Creator can find the header files.  Right click on the project and pick “Build Settings”.

Then add a path to the library in the “Additional Include Directories”

Before we fix up the library to work, I always like to hit build to make sure everything is working.

Implementing the Hardware Abstraction Layer

In order to use the driver you need a Hardware Abstraction Layer.  After looking around a little bit I find the “.ino” file which is the Arduino main project file.  In that file, the first thing that they do is declare a structure of type “struct gtt_device”.  All of the function calls to the library take a pointer to this structure.  OK.  Lets have a look at the structure

  1. First it appears that they let you store some generic data via a “Context”
  2. Then there are two functions to read and write data
  3. Then some private stuff (which is used by the packet parser)
typedef struct gtt_device
{
	void* Context;            /* device depended storage */
	gtt_write Write;          /* Function for writing data */
	gtt_read Read;            /* Function for reading data */
  

	uint8_t secured_packets;  /* 0 = regular protocol, 1 = wrap all outgoing packets with crc protection*/
	
	/* The fields below are internal and shall NOT be used by the read/write functions */
	
	gtt_parser Parser;        /* Protocol parser data */
	uint8_t *rx_buffer;       /* Buffer for incoming data */
	size_t rx_buffer_size;    /* size of the rx buffer in elements */
	uint8_t *tx_buffer;       /* Buffer for outgoing data */
	size_t tx_buffer_size;    /* size of the tx buffer in elements */
	size_t tx_index;          /* current index for the packet writer */
	gtt_events events;        /* Event Callbacks */
	size_t wait_idx;          /* Current Packet Index for the waitlist */
	gtt_waitlist_item waitlist[8]; /* Packet recieve waitlists */
} gtt_device;

That means you need to provide a function called “Write” and one called “Read” which reads bytes from the serial interface.  Here is how they setup the structure for the Arduino Demo.  Apparently they are going to make two functions called i2cWrite and i2cRead.

  gtt.Write = i2cWrite; //Set the write function
  gtt.Read = i2cRead; //Set the read function
  gtt.rx_buffer = rx_buffer; //Declare a buffer for input data
  gtt.rx_buffer_size = sizeof(rx_buffer); //Declare the size of the input buffer
  gtt.tx_buffer = tx_buffer; //Declare a buffer for output data
  gtt.tx_buffer_size = sizeof(tx_buffer); //Declare the size of the output buffer

So, what do those functions look like?

The I2C Write function just uses the Arduino Wire library to send bytes out the I2C.  And the I2C read function just reads one byte from the I2C and returns it.  OK I know how to do that on the PSoC

}     

int i2cWrite(gtt_device* gtt_device, char* data, byte data_length) {//Write an array of bytes over i2c
  Wire.beginTransmission(I2C_Address);  
  for (int i = 0; i < data_length; i++) {
    Wire.write(data[i]);        
  }
  Wire.endTransmission();  
  return 0;
}

byte i2cRead(gtt_device* gtt_device) { //Wait for one byte to be read over i2c  
  byte data;
  Wire.beginTransmission(I2C_Address);  
  Wire.requestFrom(I2C_Address, 1);     
  if(Wire.available()<1) 
  {
    return -1;
  }
  else{
    data = Wire.read();  
    Serial.println(data);
    return data;
  } 
}

To do this exact same thing on the PSoC do this.  Notice that I put in a little bit of error checking.  I also make complete legal I2C transactions, Start, Address, R/W, bytes, Stop

int generic_write(gtt_device *device, uint8_t *data, size_t length)
{
    (void)device;
    uint32 returncode;
    
   
    sprintf(buff,"length = %d ",length);
    UART_UartPutString(buff);

            
    returncode = I2C_I2CMasterSendStart( ((i2cContext_t *)device->Context)->slaveAddress,I2C_I2C_WRITE_XFER_MODE , ((i2cContext_t *)device->Context)->timeout);
    if(returncode != I2C_I2C_MSTR_NO_ERROR)
    {
        sprintf(buff,"error = %X\r\n",(unsigned int)returncode);
        UART_UartPutString(buff);
    }
    
    for(size_t i=0;i<length;i++)
    {
        I2C_I2CMasterWriteByte(data[i],((i2cContext_t *)device->Context)->timeout);
        sprintf(buff,"%d ",data[i]);
        UART_UartPutString(buff);
    }
    
    I2C_I2CMasterSendStop(((i2cContext_t *)device->Context)->timeout);
    UART_UartPutString("\r\n");
    return length;
        
}

And to make the PSoC read one byte at a time from the I2C do this … notice for some reason I didnt put in error checking.

int generic_read(gtt_device *device)
{
    (void)device;
     uint8 data;
     
    //uint32 returncode;
    I2C_I2CMasterSendStart( ((i2cContext_t *)device->Context)->slaveAddress,I2C_I2C_READ_XFER_MODE,((i2cContext_t *)device->Context)->timeout);
    I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA,&data,((i2cContext_t *)device->Context)->timeout);
    I2C_I2CMasterSendStop(((i2cContext_t *)device->Context)->timeout);
    return data;
}

Matrix Orbital Parser

The Matrix Orbital Parser is build around a function which you are supposed to call every time through your main loop.

uint8_t gtt_parser_process(gtt_device *device)

If you look in this function you will find a state machine that calls the Read function pointer, then based on the value read and the state of the state machine read in the packet.  Remember that there are two somewhat different packet formats, and this thing handles it.

uint8_t gtt_parser_process(gtt_device *device)
{
	int Res = device->Read(device);
	if (Res != -1)
	{
		switch (device->Parser.state)
		{
		case GTT_PARSER_IDLE:
			if (Res == 252)
				device->Parser.state = GTT_PARSER_COMMAND;
			else if (Res == 0) // Ignore 0's 
            {
                return 0;
            }

I notice in gtt_parser.h that they decided to use #defines for the states, which works, but would have been better done with an enumerated datatype.

#define GTT_PARSER_IDLE       0
#define GTT_PARSER_COMMAND    1
#define GTT_PARSER_LENGTH_1   2
#define GTT_PARSER_LENGTH_2   3
#define GTT_PARSER_DATA       4

One thing that took a little but of looking at is how they handle the packets that come in.  Remember from the previous articles that there are three possible sources of packets that you might read from the buffer.

  1. Packets that were generated by the configuration scripts residing in the display
  2. Packets that are generated from the user of the display doing something e.g. pressing a button
  3. Packets that are responses to the application sending it packets (e.g. get slider value)

They handle this by keeping a list of packets that are going to elicit a response.

The bottom line is that all of their code appears to do the right thing, and all you need to do is call the parser.

Add Test Project Code to main.c

I turns out that I am writing this Article after I already did all of the work for the next one where I replace the byte-by-byte parser with my own packet processor.  In order to make that work, I #ifdef to select which packet processor to use.

#ifdef GTT_ORIG_PARSER
// The original Matrix Orbital Byte Based Parser
    
uint8_t gtt_parser_process(gtt_device *device)

PSoC Creator will allow you to add this to your project on the build settings dialog.  If you click on the compiler option, you can then add defines to the command line on the “Preprocessor Definitions” box.  Notice that I added “GTT_ORIG_PARSER”

OK now the punchline.  In the main loop you need to startup the I2C, UART and setup the GTT interface.  Then you loop infinitely.  Read a key from the keyboard and do something based on what they press.

You can see that I call a bunch of their driver functions which all start with “gtt_”.

int main()
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    I2C_Start();
    UART_Start();
    UART_UartPutString("Started\r\n");
    
    gtt_device *gtt = &gtt_device_instance;
    char c;
    int16_t val;
    while (1)
    {
        c = UART_UartGetChar();
        switch(c)
        {
            case 0:  break;
            case 'l':   gtt_draw_line(gtt, 0, 0, 480, 272);  break; 
            case 'c': gtt_clear_screen(gtt);  break;
            case 'R':  gtt_reset(gtt);      break;                  
            case 'v':
                gtt25_get_gauge_value(gtt,9,&val);
                sprintf(buff,"Gauge Value = %d\r\n",val);
                UART_UartPutString(buff);
                break;
            case 'z':
                UART_UartPutString("System Mode = IDLE\r\n");
                systemMode = MODE_IDLE;
                break;
            case 'Z':
                UART_UartPutString("System Mode = POLLING\r\n");
                systemMode = MODE_POLLING;
            break;
            case '?':
                UART_UartPutString("-------- GTT Display Functions -------\r\n");
                UART_UartPutString("l\tDraw a line\r\n");
                UART_UartPutString("c\tClear Screen\r\n");
                UART_UartPutString("R\tReset\r\n");
                UART_UartPutString("v\tGet and print value of gauge \r\n");
                UART_UartPutString("-------- System Control Functions -------\r\n");
                UART_UartPutString("z\tSystemMode = IDLE\r\n");
                UART_UartPutString("Z\tSystemMode = POLLING\r\n");
            break;    
        }
        if(systemMode == MODE_POLLING)
            gtt_parser_process(gtt);
                
    }
    return 0;
}

In the next article I am going to replace their “gtt_parser_process” with a complete packet reader.

You can "git" these projects from

https://github.com/iotexpert/GTT43A

And the driver library from 

https://github.com/iotexpert/GTT-Client-Library

Title
Matrix Orbital GTT43: A Cool Display
Matrix Orbital GTT43A: Serial Interface
Matrix Orbital GTT43A: GTT Scripts
Matrix Orbital GTT43A: A PSoC 4 Interface
Matrix Orbital GTT43A: Debugging the I2C
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: PSoC 6 using RTOS and the GTT Driver Library

Matrix Orbital GTT43A: PSoC 4 Interface

Summary

In the last several articles I have written about how to use and talk to the Matrix Orbital GTT43A.  Now it is time to write some code.  The PSoC4 program that I am going to show you has evolved over time as I added stuff to it.  For instance, while I was working on this program I ran into a problem where the I2C Bus would hang (the subject of the next article).  As such, some of the code that is in this program was written to help me debug that problem.  With that said, I wanted a program that:

  1. Is command line driven i.e. I can interact with my program via serial commands through the PC COM Port
  2. Can read 1 byte at a time (like I do on the bridge control panel)
  3. Can test the “read whole packet” code
  4. Can selectively send commands via a I2C or the UART
  5. Send test commands to the display e.g. reset, clear
  6. Test the display generated messages (like button presses)

All of this code was built to run on the CY8CKIT-044 which has a PSoC 4200M MCU

Schematic & Pin Assignment

All PSoC 4 projects start with a schematic.  In my schematic I have a UART setup to talk to the KitProg (called UART) and serve as the command processor, an I2C which directly attaches to the I2C bus that drives the display, and a UART that is also attached to the display which I called SCRUART.

The I2CFAIL pin in the schematic I used to help me debug the I2C problem (the subject of the next article)

PSoC 4200m Schematic

The pin assignment has the UART attached to the KitProg UART, the I2C attached to KitProg and the Display.  The SCRUART is attached to UART on the Display.

PSoC 4200M Pin Assignment

 

Main Event Loop

The main event loop has the following parts

  1. Read a character from the keyboard
  2. Process the keyboard command character
  3. Read data from the screen
    1. If you are in packet mode read a whole packet
    2. If you are in streaming mode read one byte

There are three system modes.

  1. IDLE = Dont read from the screen
  2. PACKET = Read whole packets (poll for complete packets)
  3. STREAMING = read bytes (polling)

I also have setup the program to read/write bytes to the UART and I2C.  This is called the “comInterface”.

The enumerated type systemMode is used to setup the polling mode (each time through the main loop, what does it do?).

typedef enum {
    MODE_IDLE,
    MODE_PACKET,
    MODE_STREAMING
} systemMode_t;

systemMode_t systemMode=MODE_IDLE;

typedef enum {
    INTERFACE_I2C,
    INTERFACE_UART
} cominterface_t;

cominterface_t comInterface=INTERFACE_I2C;

The actual main section of the code

  1. Starts by initializing the UART, SCRUART and I2C.
  2. On line 299 it reads a character, then switches on the character to figure out which command the user has type.

You can see that ‘u’ and ‘U’ change the communication interface from UART to I2C and back.

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    uint32 returncode;
    
    UART_Start();
    UART_UartPutString("Started\r\n");
    I2C_Start();
    SCRUART_Start();
    
    char c;
     
    for(;;)
    {
        c = UART_UartGetChar();
        switch(c)
        {
            case 0:
            break;
            
            case 'u':
                UART_UartPutString("I2C Mode\r\n");
                comInterface = INTERFACE_I2C;
            break;
            
            case 'U':
                UART_UartPutString("UART Mode\r\n");
                comInterface = INTERFACE_UART;
            break;

The end of the loop handles the “?” case… in other words print out all of the commands.  Then based on the system mode, it either reads a whole message packet from the display or it reads only byte.

           case '?':
                UART_UartPutString("------Communication Mode------\r\n");
                UART_UartPutString("u\tI2C Mode\r\n");
                UART_UartPutString("U\tUART Mode\r\n");
                
                UART_UartPutString("------GTT43A Commands------\r\n");
                UART_UartPutString("N\tDefault Comm None\r\n");
                UART_UartPutString("I\tDefault Comm I2C\r\n");
                UART_UartPutString("S\tDefault Comm Serial\r\n");
                
                UART_UartPutString("e\tEcho abc\r\n");
                UART_UartPutString("R\tReset\r\n");
                UART_UartPutString("c\tSend Clear Screen\r\n");
                
                UART_UartPutString("------Communcation Commands------\r\n");
                UART_UartPutString("r\tRead one byte if IDLE\r\n");
                UART_UartPutString("p\tRead Packet if IDLE\r\n");
                
                UART_UartPutString("------System Mode------\r\n");
                UART_UartPutString("0\tTurn I2C polling off \r\n");
                UART_UartPutString("1\tTurn on I2C packet polling\r\n");
                UART_UartPutString("2\tRead i2c bytes \r\n");
                
                UART_UartPutString("------I2C Debugging------\r\n");
                UART_UartPutString("s\tPrint SCB Status\r\n");
                UART_UartPutString("z\tSend I2C Reset Sequence\r\n");
                UART_UartPutString("x\tPrint I2C SCL and SDA value\r\n");
            break;
        }
        
        switch(systemMode)
        {
            case MODE_IDLE:
            break;
            
            case MODE_PACKET:
                readPacket();
            break;
                
            case MODE_STREAMING:
                readByte();
            break;
        }

I created three keys (0,1,2) to change the system mode, from IDLE, to reading whole packets to reading bytes.

            // System Modes
            case '0':
                    I2CFAIL_Write(0);
                    UART_UartPutString("Packet Poling Off\r\n");
                    systemMode = MODE_IDLE;
                break;
               
            case '1':
                    I2CFAIL_Write(0);
                    UART_UartPutString("Packet Poling On\r\n");
                    systemMode = MODE_PACKET;
            break;
            case '2':
                    I2CFAIL_Write(0);
                    UART_UartPutString("Read continuous\r\n");
                    systemMode = MODE_STREAMING;
                    break;

While I was trying to figure out how things worked I wanted to be able to do one thing at a time. So I create ‘r’ to read one byte (like Bridge Control Panel) and ‘p’ to read a whole packet.  Notice that you really really only want to do this why you are not polling the display.

            // If you are IDLE you can read 1 byte with 'r' or read a whole packet with 'p'
            case 'r':  // Read byte
                if(systemMode != MODE_IDLE)
                    break;
                readByte(&data);
                
            break;
            case 'p': // read packet
                if(systemMode == MODE_IDLE)
                    readPacketI2C();
            break;

The last section of commands send various GTT2.0 commands to the display.  Notice that the writePacket function knows which system interface to use (either I2C or UART).

First, I declare some commands, just an array of bytes.

// These commands come the GTT 2.0 and GTT2.5 Protocol Manuals
uint8 clearCMD[] = { 0x58 };
uint8 resetCMD[] = { 0x01};
uint8 comI2CCMD[] = { 0x05, 0x02};
uint8 comNONECMD[] = { 0x05, 0x00};
uint8 comSERIALCMD[] = { 0x05, 0x01};
uint8 comECHOCMD[] = {0xFF,'a','b','c', 0};

Then I use them:

            case 'e':
                UART_UartPutString("Send Echo Command\r\n");
                writePacket(sizeof(comECHOCMD) , comECHOCMD);
            break;
                
            case 'c':
                UART_UartPutString("Sent Clear String\r\n");
                writePacket(sizeof(clearCMD),clearCMD);
            break;
            case 'R':
                UART_UartPutString("Sent Reset String\r\n");
                writePacket(sizeof(resetCMD),resetCMD);
            break;
            case 'I':
                UART_UartPutString("I2C Communcation Channel\r\n");
                writePacket(sizeof(comI2CCMD),comI2CCMD);
            break;
            case 'N':
                UART_UartPutString("NONE Communcation Channel\r\n");
                writePacket(sizeof(comNONECMD),comNONECMD);
            break;

Read Byte

In order to read one byte from the display I first determine which mode Im in, then call the appropriate sub-function.

uint32_t readByte(uint8_t *data)
{
    uint32_t returncode=0;
    switch(comInterface)
    {
        case INTERFACE_I2C:
            returncode = readByteI2C(data);
        break;
        case INTERFACE_UART:
            returncode = readByteUART(data);
        break;
    }
    sprintf(buff,"Returncode = %X Data=%d\r\n",(unsigned int)returncode,*data);
    UART_UartPutString(buff);

    return returncode;
}

The first sub function to read bytes via I2C.  To read a byte with no error checking you have to

  1. Send a Start
  2. Send the address
  3. Send the Read bit
  4. Clock it 8 times (this is exactly what the I2CMasterReadByte function does)
  5. Send an NAK
  6. Send a Stop

I ran into an I2C issue which I will talk about in the next article, however, if I see an error from any of these commands Ill put the system into MODE_IDLE and throw an error.  In addition I write a 1 to the I2CFAIL pin, which I am using to trigger the Oscilliscope (so I can see what is happening)

uint32_t readByteI2C(uint8_t *data)
{
    uint32 returncode;
    returncode = I2C_I2CMasterSendStart(I2CADDR,I2C_I2C_READ_XFER_MODE , I2CTIMEOUT);
    if(returncode)
    {
        sprintf(buff,"send start error %lX status %lX\r\n",returncode,I2C_I2CMasterStatus());
        UART_UartPutString(buff);
        I2CFAIL_Write(1);
        systemMode = MODE_IDLE;
        goto cnt;
    }
            
    returncode = I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA,data,I2CTIMEOUT);
    if(returncode)
    {
        sprintf(buff,"read byte error %lX status %lX sda=%d scl =%d\r\n",returncode,I2C_I2CMasterStatus(),I2C_sda_Read(),I2C_scl_Read());
        UART_UartPutString(buff);
        I2CFAIL_Write(1);
        systemMode = MODE_IDLE;
        goto cnt;
    }
            
    returncode = I2C_I2CMasterSendStop(I2CTIMEOUT);
    if(returncode)
    {
        sprintf(buff,"send stop error %lX status %lX\r\n",returncode,I2C_I2CMasterStatus());
        UART_UartPutString(buff);
        I2CFAIL_Write(1);
        systemMode = MODE_IDLE;
        goto cnt;
    }
    
    cnt:
    return returncode;
}

Read Packet

For the packet read code I did the same thing as the byte read code.  Specifically I wrote an overall get packet, then called the correct read packet based on the

If you read the source code that Matrix Orbital gives you for drivers, you will find that it reads one byte at a time.  The problem with doing this is that you

  1. Send a start
  2. Send an I2C address
  3. Send a read bit
  4. Read the ACK
  5. Read a byte
  6. Send a NAK
  7. Send a stop

The problem with this approach is that it uses 11 bit-times extra per byte of overhead (steps 1-4) which kinda sucks.  So I wanted to write a complete packet reader.  My packet reader will

  1. Send a start
  2. Send an I2C address
  3. Send a read bit
  4. Read the ACK
  5. Read a byte  [This is the 254 that marks the start of the packet]
  6. ACK
  7. Read a byte [This is the command which identifies the packet]
  8. ACK
  9. Read a byte [The MSB of the Length]
  10. ACK
  11. Read a byte [The LSB of the Length]
  12. NAK
  13. If there is a length then:
  14. Send the start
  15. Send an I2C address
  16. Send a read bit
  17. Read the ACK
  18. read length -1 bytes
  19. ACK
  20. Read the last byte
  21. Send a NAK
  22. Send a stop

By spec you are supposed to NAK your last read byte to indicate that your read transaction is over… that means you have to NAK the last Length byte because there could be 0 bytes to read, in which case you would need to stop.  It would have been nice if the protocol let you send only one start, but Im pretty sure it was designed for UART, which doesn’t suffer from this problem.  Also as a side note, Im pretty sure that the MCU they are using doesn’t really care, but Im not willing to implement it incorrectly.

Here is the code:

void readPacketI2C()
{
    int length;
    int command;
    uint8_t data;
    uint32_t returncode;
    
    returncode = I2C_I2CMasterSendStart(I2CADDR,I2C_I2C_READ_XFER_MODE , I2CTIMEOUT);
    returncode |= I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA,&data,I2CTIMEOUT);
    returncode |= I2C_I2CMasterSendStop(I2CTIMEOUT);
    
    // Something bad happened on the I2C Bus ....
    if(returncode)
    {
        systemMode = MODE_IDLE; 
        sprintf(buff,"I2C Return Code %X\r\n",(unsigned int)returncode);
        UART_UartPutString(buff);
    }
 
    // The screen returns a 0 when there is nothing in the buffer.
    if(data == 0)
    {
        return;
    }

    // This is bad because there was something other than a packet start byte
    if(data != 252)
    {
        sprintf(buff,"bad data = %d\r\n",data);
        UART_UartPutString(buff);
        systemMode = MODE_IDLE; // put it into nothing mode...
        return;
    }
    
    // We know that we have a command
    returncode = I2C_I2CMasterSendStart(I2CADDR,I2C_I2C_READ_XFER_MODE , I2CTIMEOUT);
    returncode |= I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA,&data,I2CTIMEOUT); // command
    command = data;
    
    returncode |= I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA,&data,I2CTIMEOUT); // length
    length = data<<8;
    returncode |= I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA,&data,I2CTIMEOUT); // length
    length = length + data;
    returncode |= I2C_I2CMasterSendStop(I2CTIMEOUT);
    
    // If the packet has any data... then read it.
    if(length != 0)
    {
        returncode |= I2C_I2CMasterSendStart(I2CADDR,I2C_I2C_READ_XFER_MODE , I2CTIMEOUT);
    
        for(int i=0;i<length-1; i++)
        {
            I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA,&data,I2CTIMEOUT); // length
            inbuff[i] = data;
        }

        // Read the last byte
        I2C_I2CMasterReadByte(I2C_I2C_NAK_DATA,&data,I2CTIMEOUT); // length
        inbuff[length-1] = data;
        returncode |= I2C_I2CMasterSendStop(I2CTIMEOUT);
        
        I2C_I2CMasterSendStop(I2CTIMEOUT);
    }
      
    sprintf(buff,"command = %d length = %d bytes= ",command,length);
    UART_UartPutString(buff);
    for(int i=0;i<length;i++)
    {
        sprintf(buff,"%d ",inbuff[i]);
        UART_UartPutString(buff);
    }
    UART_UartPutString("\r\n");
    
}

You can "git" these projects from

https://github.com/iotexpert/GTT43A

And the driver library from 

https://github.com/iotexpert/GTT-Client-Library

Title
Matrix Orbital GTT43: A Cool Display
Matrix Orbital GTT43A: Serial Interface
Matrix Orbital GTT43A: GTT Scripts
Matrix Orbital GTT43A: A PSoC 4 Interface
Matrix Orbital GTT43A: Debugging the I2C
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: PSoC 6 using RTOS and the GTT Driver Library

Matrix Orbital GTT43A: GTT Scripts

Summary

As I have been working my way through understanding the Matrix Orbital displays, I think that the whole scheme for programming them is very clever.  The bottom line of this article is that Matrix Orbital created a command language for making things work in the display, then the built the rest of their toolset around that language.  In this article Ill show you how it is all connected.

A Buffer Full of Mystery

While I was trying to understand how the GTT Support Tool works, I send a “Reset Module” also known as {254, 1}.  When I ran the script, the screen rebooted, and then the serial monitor dumped out a boat load of stuff.  You can see it in the picture below:

This was not very different than when I first attached to the screen using the Bridge Control Panel.  Here are the first I2C reads after the reboot.

So, what is all of this stuff?  The answer to that question resides in how this whole thing is put together.

GTT Scripts

All interactions with the display are done via the command language.  There are multiple paths for sending commands to the screen, the ones we have talked about so far, I2C, USB and UART.  And the one path that I have not specifically talked about but works exactly the same way, the mass storage sd-card.

When you design a screen in GTT Designer, what it does is take that screen, and spit out a text based command language.  Here is a screenshot of that for the test project that I am working on.

Then GTT Designer compiles the text into a binary file with the GTT2.0 and GTT2.5 commands.  You can look at the binary file with a binary file editor.  Here it is for Screen1.bin

And you should recognize the bytes you see.  Look at the top and you will see {FE, 05, 00} which in decimal is {254, 05, 00} and if you look in the GTT20 manual you will find that is “Set Communication Channel to None”.  Here is the screenshot from the manual.

But, what happens when the device boots?  Well, they followed the lead from MS-DOS and created a file called “autoexec”.  And, if you look in that file they so graciously tell you what happens.

How cool is that?  All of these commands are just the things that you did on the project setting and the display settings.  And the last line of the file launches just launches Screen1.bin, which is just the binary file of the commands it takes to load the Screen1.

Now back to the original question.  What is the buffer full of mystery?  Simple, it is the output of all of the commands (if they make output) that are in the autoexec binary and the screen1 binary.  If you had happened to set the default channel to “none” you will find that the buffer doesnt have anything in it… which should make sense.

So, when the manual says to delete the autoexec at the top level directory in order to reset the board.  All that does it remove all of the settings that you created in your project.

The only thing that I wish is that they gave you access to the txt–>bin compiler.  But oh well.

You can "git" these projects from

https://github.com/iotexpert/GTT43A

And the driver library from 

https://github.com/iotexpert/GTT-Client-Library

Title
Matrix Orbital GTT43: A Cool Display
Matrix Orbital GTT43A: Serial Interface
Matrix Orbital GTT43A: GTT Scripts
Matrix Orbital GTT43A: A PSoC 4 Interface
Matrix Orbital GTT43A: Debugging the I2C
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: PSoC 6 using RTOS and the GTT Driver Library

Matrix Orbital GTT43A: Serial Interface

Summary

In this article I will show you how to interact with the Matrix Orbital GTT43A display using the GTT Support Tool via the KitProg UART on a CY8CKIT-044  and the Bridge Control Panel via I2C.  This article will be broken up into four parts

  1. Building a Test Project in GTT Designer
  2. GTT Protocol 2.0
  3. GTT Protocol 2.5
  4. GTT Support Tool
  5. Bridge Control Panel

Build a Test Project

In order to understand the whole serial interface, I will build a very simple project with two buttons.  You can find this project in my GitHub site at git@github.com:iotexpert/GTT43A

Start up a new project called “BasicTest”.  Accept the defaults for Project settings.  On the Display Settings screen change the “Default Channel” to Serial and the Flow Control to Off.  I am using the CY8CKIT-044 to talk to the screen.  On that development kit there is a KitProg which serves as a programmer (for the PSoC 4200M) and as a serial bridge.  That serial bridge does not have the flow control pins turned on.  If you leave the flow control on, the screen will never transmit and you will need to poke your eyeballs out trying to figure out why.

  

Drag a Circle Button (GTT2.5) onto the screen.  Change the Text value (on the right panel) to “GTT25”

 

Now click on the “Legacy” tab and drag a “Circle Button” onto the screen.  Then change the text label to “Legacy”.  In addition change the “ID” from “Auto” to “2”.  I noticed that later in this article that if the button ID is “auto” it will not be identified in the messages.  Im not sure if this is a bug or my lack of understanding.

When I click “Deploy” I end up with this error message.  This appears to be a bug in the GTT Designer.

Click on the “Font” and go to the directory where the Font is supposed to be… and find that it is actually empty.

So.  I navigate up a directory and then down into “LibraSans” instead of “libra-sans-modern”.

Once that is done press “Deploy”, and I get Success!

Now that I have the project built I need to wire the whole mess together.  I am going to use a CY8CKIT-044.  This development kit has a PSoC4200M.  Although the project is using a PSoC6, I didnt have any level translators at my house (bad Alan) and I wanted to get going … so I used the 5V tolerant PSoC 4. [note: it turns out that the MCU used on the display has 5V tolerant serial I/Os but is a 3.3v MCU so I could have used it directly on the PSoC 6 without this work around]  On the far left of the development kit, right next to the USB is the KitProg connector.  This connector has an I2C Master and a UART Bridge.  In addition I can connect the I2C pins to PSoC, the Display and the Bridge.  Here is the schematic for the whole mess.

When you look on the back of the CY8CKIT-044 you can see the same wiring diagram on the silkscreen.

Here is a picture of the development kit.  You can see the top green/yellow wires go to P40/P41 (that is the I2C Bus) and the other green/yellow set goes to P12[6] and P12[7] that is the KitProg UART.  I suppose I should have used different colors for I2C and UART… oh well.

And finally the whole mess.

GTT43A Protocol 2.0

They way that the system works is you can send a command data packet to the display via one of the serial interfaces and then if there is a response required,  it will respond with a response data packet.  In addition when some event occurs on the display (like a touch screen button being pressed) it will send you a response data packet.

There are two GTT Protocols, one called 2.0 and one called 2.5.  First the 2.0 protocol.  You send messages in the GTT2.0 command data packet format.  That format is simple:

  1. 254 – 1 byte
  2. Message ID (look in the manual for all of the legal message codes) – 1 byte
  3. Optional data 0-n bytes

If there is a required response, the screen will respond with a data packet in the following format

  1. 252 – 1 byte
  2. Message ID – 1 byte
  3. Length MSB – 1 byte
  4. Length LSB – 1 byte
  5. Optional Data – 0–>65535 bytes

The total length of the response will be (length msb) << 8 | length lsb – big endian.

For example a command with no optional data …. like reset will look like this { 252, 1 }. [obviously dont send the braces or the comma]

A command with some optional data is “Set Backlight Brightness”. If you want the brightness set to 23% you would send is {252, 153, 23}.

An example of a command that will respond with a response data packet is “Get Module Type” where you would send {254, 55 } and the screen would respond with {252, 55, 00 02 147 01}.  Here is the picture from the manual.

When you look at the picture above there are several things to be VERY careful about.

First, the Matrix Orbital people appear to think in decimal not hex.  So they show you {252,55,00,02,147,01) instead of (0xFE, 0x37, 0x00, 0x02, 0x93, 0x01}.  Notice that my GTT43A value is decimal 37633 which is also known as Hex 0x9301.  I  often found myself typing hex when I mean decimal and vice versa.

The second thing to be careful about is that they send values in big endian… so if you are using an ARM processor be aware as ARM is almost always little endian.

The third thing to be careful about is that the length parameter in the message is always 16-bit Big Endian even though the documentation often shortens this to “Length”

Lastly, if they send you a 16-bit value it will show up as big endian, and they will probably show it to you in decimal (like table 14 above).  Which can look kinda weird in decimal as it is two bytes.

GTT43A Protocol 2.5

As I worked on understanding what the screen is doing, I noticed that I was getting message codes that were not documented in the GTT2.0 Manual.  For example when you press a button you will get {252,235,0,5,21,0,0,1,1}.  When you look at this packet you can recognize the 252… and the message code 235 … and the length seems to make sense 5 … but the rest of the message is less clear.

So I called technical support, which is excellent, and Daniel agreed to send me a prerelease of the new manual.  When you look at the manual you will find:

Which makes good sense as I pressed a button, and the object ID was “1”.  The pre release version of the manual that I got seems to be missing information, but Im hopeful they will get it done and released soon.  But for now, I can work with it.  If you need a copy of the manual you will need to contact them directly.

GTT Support Tool

When you install GTT Designer, it will also install a program called the GTT Support Tool.  This tool will allow you to send and receive GTT messages to/from the screen.   I have the screen attached to the UART on the KitProg Serial bridge (with jumper wires).  This means I will see the screen in the Device Manger under the Ports->KitProg (see the COM5)

You can run the GTT Support Tool from the Start menu or from the Tools menu on the GTT Designer.

When the tool starts you can pick out which ComPort to talk to and setup the Flow Control (meaning use or not CTS/RTS).  The KitProg on the 4200M does not have the CTS/RTS so you need to disable it or things will not work.

When you press the “Test Connection” it will send a message via the UART/ComPort and wait for a response. I am not sure, but I suspect that it is sending a “Get Module” command.  In the picture below you can see that I got both the send and the receive message to work.

I was struggling over the weekend to get the communication to work correctly.  Specifically I was not getting return messages.  It turned out that the project that you build in the GTT Designer must have “Flow Control” turned off and the Default Channel set to Serial or the serial communication responses will not work.  Remember you can set this on the “Display Settings”

You can also send the commands to set the communication channel and flow control, but I found it easier to get the project to the correct settings in the GTT Designer.

One you have a functional connection you can click on the “Commands” tab.  On the left of the screen you will see a list of commands (well… actually on GTT2.0 commands).  On the right side of the screen you will see your “script”, which obviously starts blank.

The first thing that you should do is press the little split screen button just to the right of the red “X”.  When you do this it will bring up a new window that will show what is sent, and what is received.  Obviously it starts blank.

Now you can double click on a command, like “Get Module Version” and it will bring up the command.  When you press “OK” it will add it to the script.  You CAN change the command and it will add it the script (but with a misleading name)

Here is what the window looks like after I add the “Get Module Type” command.

In order to Run the command, you need to click in the script window and then press the green run button.  If you have not clicked in the script window it will not run. Once you have run the command your viewer window will look like this.

In the window above you can see that I wrote to the serial port a {254,55} and the screen responded {252,55,0,2,147,14}.  When you decode this message don’t forget that the data above is in decimal.  In this the example the message the 147,14 is actually 0x930E which is 37646.  Unfortunately 37646 is not documented in the GTT 2.0 protocol manual (but my screen is a GTT43A).

Another cool thing that that happens in the Debug view is that it shows events that are initiated from the screen.  For instance in the picture below I pressed the “GTT25” button from my example project.  It show the button press and the button release.

 

Bridge Control Panel

For my real project I want to interact with the GTT43A via I2C.  For this kind of thing I always like to use the Bridge Control Panel first. I have written a bunch of articles about using BCP to debug.  This allows me to act as an I2C Master and see how the device acts as a slave.  The first thing that I do is press “List Devices” on the BCP.  It shows me that there are a number of things connected to the I2C bus.

You might recall that from the Display Settings that the default I2C address is 80.

That 80 is also known as 0x50 (here we go with the Hex / Decimal thing again).  Moreover that 80 (0x50) is an 8-Bit address, in other words shifted left 1 from the 7-bit address of 0x28.

After I verified that the screen was attached to the bridge control panel.  The next thing to do was to set the default communication via I2C.  Here is the section of the documentation

This means that I need to send {0xFE, ox05, 0x02}.  When I send that command I get ACKs … good.

The next thing that I do is test to make sure that the legacy button is working correctly.  When I press it and then read some bytes I get

Then I press the GTT 25 Button and get {FC,EB,00,05,15,00,00,01,01} and then {FC,EB,00,05,14,00,00,01,00} in other words a press of button ID 1 and then a release of button ID 1.

Now that we understand the command protocol and we know how to talk to the screen, in the next article Ill talk about GTT Scripts.

You can "git" these projects from

https://github.com/iotexpert/GTT43A

And the driver library from 

https://github.com/iotexpert/GTT-Client-Library

Title
Matrix Orbital GTT43: A Cool Display
Matrix Orbital GTT43A: Serial Interface
Matrix Orbital GTT43A: GTT Scripts
Matrix Orbital GTT43A: A PSoC 4 Interface
Matrix Orbital GTT43A: Debugging the I2C
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: PSoC 6 using RTOS and the GTT Driver Library

Matrix Orbital GTT43A: A Cool Display

Summary

I have recently found myself way way down a rabbit hole on a project that uses a Matrix Orbital GTT43A.   This display is a super cool, though expensive, intelligent touch screen display.  Here is a picture:

I call it intelligent because it has a fully featured CPU that runs all of the display and touch functions for you.  Basically you build all of the screens with GTT GUI elements (sliders, buttons, text etc.) using the Matrix Orbital design tool called GTT Designer.  Then you program that configuration into an sd-card that is attached to the display.  When you power up the display, your configuration comes up and you are off to the races.  Then, in your system you can then simply interact with the display via I2C, UART, USB or SPI.

I know that it seems simple, but I will say that this has turned into quite an adventure which has, in turn, been an awesome learning experience.

Matrix Orbital GTT43A

The Matrix Orbital GTT43A is a 4.3″ backlit LCD display with a capacitive touch screen.  Here is a screenshot from the Matrix Orbital website.  Yes, you read the price right, it is $155.84…. well actually $165.84 with capacitive touch.

 

Here is a picture of the back of the screen.

On the far left you can see the connector labeled “Keyboard Power”.  This is a place where you can plug in a matrix keyboard that looks like the next picture.  Though I am not exactly sure why you would make a nice touch screen interface and then use a mechanical button interface?

In the middle of the picture you can see a micro-sd card which holds all of your screen configuration information.  The sd-card is a normal mass storage card and you can drag and drop your configuration, or new firmware for the screen using normal Windows.  You can also put the display into mass-storage mode and then access the card via the mini-usb-b connector that is in the upper left.

The display also supports 6 digital GPIOs (which you can see on the lower left of the picture).  It also has a piezoelectric buzzer and a haptic vibrator.

In order to talk to the display with your system controller you can use I2C, UART, SPI or USB.  It is interesting that you seem to be able to use multiple interfaces at the same time, which is pretty convenient for debugging.

The display requires a decent amount of juice.  Here is a picture on my desk, 5V and 375mA,  which is more than the development kit I was using will provide.  In fact it, will sort of work for a while off the devkit power supply, but when you push a button on the screen it will reboot the display.

GTT Designer

GTT Designer is a Windows GUI building tool.  It is straight forward to use.  When you start up the software it will give you a choice of displays to build for.  In the picture below you can see that it detected that I had a GTT43A attached to my computer via USB.

 

After setting up the name of my project and clicking “New Project” I am given the choice of customizing the global settings for the display.

Once the project is setup, you are now given the ability to configure the display settings.  On this screen you can setup a number of things, including the I2C address of the display.  Also, on the screen below you can see “Default Channel” is set to none.  What this means is that any GUI thing that happens will send messages to the default channel.  In this case none.  But that isnt what I want so I change it to I2C (next picture)

What I really want it all of the output to go to the I2C.  But given that the screen is an I2C slave, and it cant send out data, what does that really mean?  What it means is that all of the output goes into a buffer, that slowly fills up until you read the data out of it via I2C.  If you setup the Default channel as Serial, the data will go directly out via UART.

Once the display settings are done you will end up with a screen that looks like this.  On the left side of the screen you can pick out the different GUI elements (buttons, text labels, sliders, images etc) and the drag them onto the screen.  Notice that there are four tabs, Tools, Legacy Tools, Assets, Overview.  At some point very recently Matrix Orbital did a massive re-engineering project to make things simpler to interact with the screen.  When they did this, the created a who new set of widgets called “GTT2.5” widgets.  You can still use the old widgets which they now call “Legacy”

On the screen below you can see that I placed a bunch of different GUI elements for my test project.  When you click on an element, the right hand side of the screen will let you update properties of the object e.g. color, size, name.  You can also create events (more on that in the next article)

One you have drawn your screen you then want to build and program the project.  Or in their language generate and deploy.  To do this you can either click generate then click deploy, or just click deploy.  When you do this it will first build all of your project into a directory on your computer called “Output”.  There are three interesting things in the output directory.

  1. autoexec.txt/bin – files that contains a script that runs when the display turns on (more on that in the next article)
  2. Report.txt – a file that contains information about the objects, names, ids etc (this is important for your software)
  3. GTTProject1 – a directory with all of the files required for your project.

When you look in the GTTProject1 directory you will see that it contains a directory for “Screen1”.  If I had made multiple screens it would have made multiple directories.  It also has a directory called “Fonts”, which big surprise, contains the Fonts that are used by the project.

In the “Screen1” directory you will see a bunch of bitmap files, text files etc.

Screen1.txt contains a textual version of the “program” that creates the screen.  Here is a snapshot of the top of the file.

And “Screen1.bin” which is the compiled version of the “Screen1.txt”- more on this in the next article.

When you click the deploy button, it sends a command to the screen to put it into mass storage mode which just turns the screen into a flash disk which can be written/read by your PC.  Here is what the screen looks like when it is in mass storage mode:

After the device is in mass storage mode, GTT Designer copies all of the file onto the sd-card of the display and the reboots the display to run the program.

When you are running GTT Designer you can switch the display back and forth between Mass Storage mode and Display mode on the Tools menu by selecting “Switch Mode”

In the next several articles Ill show you how to build firmware to talk to the screen.

You can "git" these projects from

https://github.com/iotexpert/GTT43A

And the driver library from 

https://github.com/iotexpert/GTT-Client-Library

Title
Matrix Orbital GTT43: A Cool Display
Matrix Orbital GTT43A: Serial Interface
Matrix Orbital GTT43A: GTT Scripts
Matrix Orbital GTT43A: A PSoC 4 Interface
Matrix Orbital GTT43A: Debugging the I2C
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: GTT Driver Library - Part 1
Matrix Orbital GTT43A: PSoC 6 using RTOS and the GTT Driver Library