Newhaven Display Forum

Newhaven Products => OLEDs => Topic started by: Kon-L on September 30, 2017, 05:06:48 AM

Title: OLED Multi-Font IC Problems
Post by: Kon-L on September 30, 2017, 05:06:48 AM
Hello folks,

I have been working for some time now with the NHD-3.12-25664UMB3.  It is a great display.  However I have yet to be able to use the Multi-font functionality.  I wrote a program based on the datasheets sample program, but nothing will display.  Everything else in my program works and will display, but when it comes time to use the Multi-font IC the display stays blank.  It was the main reason this display was chosen was to keep from having to write the code for multiple languages.  Storing the Japanese or Chinese fonts takes up too much memory space in my PIC chip.  I am using 4 wire SPI connection with a PIC18F.  Below is the relevant code for those who are interested, but I have yet to get it to work.  Does anyone have any suggestion to get it to work?  Has anyone ever got it to work?


/*********************************************************************
Function Name:  SetOLEDAddress                                               
Return Value:                                             
Parameters:  oled_column, oled_row                       
Description: sets pointer to to write/read operations
********************************************************************/
void SetOLEDAddress(unsigned char x, unsigned char y){         //Starts at 0, 0 going to 255, 63
   
    OLED_command(0x15);    //column command
   OLED_data(x);
   OLED_data(0x77);           //total columns divided by 2 (Default 77)
   
    OLED_command(0x75);    //row command
   OLED_data(y);
   OLED_data(0x7F);           // default 7F
}


/*************************************************************
   Function Name:  Delay_ms                                         
   Return Value:   void                                           
   Parameters:     Number of milliseconds                   
   Description:    Delay function
                   TMR1 times out every ~170.666 us     
**************************************************************/
void Delay_ms(long num_ms){                                          //JFR Used in the Key Scan function
   long n;                                             
 
   for(n = 0; n < num_ms; n++){
      INTCONbits.TMR0IF = 0;      // Clear flag
      TMR0H = 0xFC;                                                //We only count from FC18h to FFFF
      TMR0L = 0x1A;                                                               //Deducting one because each line takes time to execute also
      while(!INTCONbits.TMR0IF);  // Wait for timer flag to set
   }
}




/******************************************************************
   Function Name:    OLED_char                               
   Return Value:                                             
   Parameters:       character, oled_column, oled_row                       
   Description:      Gets character data from the Multi-Font IC of the OLED Disable
******************************************************************/
void OLED_char (unsigned char character, unsigned char oled_column, unsigned char oled_row, char line){

   unsigned char MSB, LSB, byte1, byte2, byte3;
   unsigned char readByte, i;
    unsigned char *readByte_ptr;
   unsigned char FontBuffer[4][32];               //5 for 5 lines of text. 1-4 are the lines and 5 is going to be volume display characters
   unsigned short long Address;                  //unsigned short long is 24-bits
   //Need to split character into 2 8-bit words MSB and LSB   
   MSB = (character >> 8) & 0xFF;                  //Could define each or the array as int or char or whatever
   LSB = character & 0xFF;
   
    //readByte_ptr = &readByte;
   
   //Parameters: JIS0208 Japanese                                 
   //MSB: Higher byte of JIS code                     I think we should use Japanese JIS 0208
   //LSB: Lower byte of JIS code                     This will include English and the Infinity symbol
   //Address: Address of character data               Note though ALL characters will be double height 15x16 pixels
   if((MSB >= 1 && MSB <= 94) && (LSB >= 1 && LSB <= 94))
   Address = (((((MSB - 1) * 94) + (LSB - 1)) * 32) + 490,624);
   
   //To split the resulting number into bytes
   byte1 = (Address >> 16) & 0xFF;               
   byte2 = (Address >> 8) & 0xFF;            //uses Bitwise AND to form 8-bit values
   byte3 = Address & 0xFF;      //source: https://stackoverflow.com/questions/3784263/converting-an-int-into-a-4-byte-char-array-c
   
   //Send READ command and Address to Multi-Font IC.
    OLEDen = 1;
   MuFen = 0;                  //make rc5 of PIC18 go low to enable the Multi-Font IC
   MultiFont_OUT(0x0B); //READ command                  This SPI_OUT is for the Multi-Font IC NOT the OLED
   MultiFont_OUT(byte1); //Address Byte1 (MSB)      
   MultiFont_OUT(byte2); //Address Byte2            
   MultiFont_OUT(byte3); //Address Byte3 (LSB)      
   MultiFont_OUT(0xFF); //Dummy Byte               Datasheet says 0x00, but their example has 0xFF
   
   
      for(i = 0 ; i < 32 ; i++ ){               //For i number of bytes to gather from Multi-Font IC.  In this case its 32.
            FontBuffer[1] = MultiFont_IN();         //JFR Modified for testing!!!
      }   
   
   MuFen = 1;               //make rc5 of PIC18 go high to disable the Multi-Font IC
   OLEDen = 0;
   
      SetOLEDAddress(oled_column, oled_row);          //set address location for font to be displayed
      wram();                                                            //Write to OLED Ram instruction
      for(i = 0 ; i < 16 ; i++ ){                //for the first 16 bytes of font data
         OLED_data(FontBuffer[1]);       //write byte to display
         oled_column++;                            //next byte will be for the next column
         SetOLEDAddress(oled_column, oled_row);       //set address location for next column
         wram();
      }
   
      oled_column = oled_column - 16;                      //go back to first column of the font
      oled_row = oled_row + 8;                         //move down to show the bottom half of font  example had +16
      SetOLEDAddress(oled_column, oled_row);          //set address for bottom half of font
         
      for(i = 16 ; i < 32 ; i++ ){                //for the second 16 bytes of font data
         wram();
         OLED_data(FontBuffer[1]);       //write byte to display
         oled_column++;                            //next byte will be for the next column
         SetOLEDAddress(oled_column, oled_row);       //set address location for next column
         
      }
      
}

/******************************************************************
   Function Name: display_oled_init                                         
   Return Value:  1 (used in main initialization routine)                                           
   Parameters:    void                     
   Description:   Detects the OLED display and initializes it 
*******************************************************************/
void display_oled_init(void)
{
    unsigned char m;
    unsigned int i;
    unsigned char n;
    unsigned char character;
    unsigned char charcolumn;
   unsigned char num_char = 16;               //Maximum characters per line.
   
   //Delay_ms(250);
   
    //oled_setup();

    int init_text[2][16] = {{0x2121, 0x2121, 0x2349, 0x236E, 0x2369, 0x2374, 0x2369, 0x2361, 0x236C, 0x2369, 0x237A, 0x2369, 0x236E, 0x2367, 0x2121, 0x2121},
                     {0x2121, 0x2350, 0x236C, 0x2365, 0x2361, 0x2373, 0x2365, 0x2124, 0x2121, 0x2377, 0x2361, 0x2369, 0x2374, 0x2125, 0x2121, 0x2121}};
                                 //Displays 'Intializing' on line 2, and 'Please, wait.' on line 3.
                                 //line 3 needs to be shifted by half a character to center under line 2.
   
   SetOLEDAddress(0,16);      //First the 'Initializing' line.
   
   for(n = 0; n < num_char; n++){            //num_char should be changed to the exact number of characters in line_text
        character = init_text[1][n];      //JFR Changed for testing!!!
      charcolumn = (n * 16);
      OLED_char(character, charcolumn, 16, 1);      
   }
   
   //for(n = 0; n < num_char; n++){            //num_char should be changed to the exact number of characters in line_text
      //  character = init_text[0][n];
      //OLED_char(character, 0, 32, 1);      
   //}
   
   SetOLEDAddress(8,32);      //Then the 'Please, wait.' line. Shift it right by half a charater.  Thats 8 pixels.

   //for(n = 0; n < num_char; n++){            //num_char should be changed to the exact number of characters in line_text
    //    character = init_text[1][n];
   //   OLED_char(character, 0, 16, 2);      
   //}
   
   for(n = 0; n < num_char; n++){            //num_char should be changed to the exact number of characters in line_text
        character = init_text[1][n];
      OLED_char(character, 0, 32, 2);      
   }
   //Would like to have these two lines centered.  Can do this by adding blank spaces in front of each line.
   Delay_ms(750);
   
   //clear_oled_display();            //clears oled display
}
Title: Re: OLED Multi-Font IC Problems
Post by: Kon-L on November 10, 2017, 01:23:13 AM
Well my good folks, I have figured this out on my own.  Below are the relevant sections, but I will also post my full code for you to try! I am using a PIC18F26J50 for this project with a 16MHz crystal and NHD-2.8-25664UMB3 in 4 wire serial mode(SPI).  It is compiled using Microchip's XC8 compiler. Hope this helps others! I know I wrestled with it for too long myself.



/******************************************************************
   Function Name:  OLED_Init                                       
   Return Value:                                             
   Parameters:                         
   Description:    Initializes OLED Display
******************************************************************/
void OLED_Init(void){
   
   RES = 1;
   Delay_ms(200);
   RES = 0;
   Delay_ms(750);
   RES = 1;
   Delay_ms(750);
   
   comlock(0x12);
   onoff(0x00);
   column(0x1c,0x5B);
   row(0x00,0x3f);
   disclock(0x91);
   ratio(0x3F);
   offset(0x00);
   start(0x00);
   remap(0x14);    /*set in Horizontal incriment mode aka Row Major Addressing*/
   GPIO(0x00);
   funsel(0x01);
   disen(0xA0, 0xFD);
   contrast(0x9F);
   master(0x0F);
   LGST();
   phase(0xE2);
   disenb(0x20);
   prevolt(0x1F);
   preper(0x08);
   vcom(0x07);
   mode(0x02);
   partial(0x01, 0x00 ,0x00);
   Clear_OLED();
   onoff(0x01);
}


void column(unsigned char a, unsigned char b) {            //
   OLED_command(0x15);                 //Set Column Address
   OLED_data(a);                    //Default - 00
   OLED_data(b);                    //total columns divided by 2 (Default 77)
}

void row(unsigned char a, unsigned char b) {            //
   OLED_command(0x75);                 // Row Address
   OLED_data(a);                    // default 00
   OLED_data(b);                       // default 7F
}

void wram(void){                  //Write to RAM command
   OLED_command(0x5c);
}


/******************************************************************
   Function Name:    Clear_OLED                               
   Return Value:                                             
   Parameters:                            
   Description:      Clears OLED display
******************************************************************/
void Clear_OLED(void) {
   unsigned char i,j;
 
   column(0x00,0x77);
   row(0x00,0x7F);
   wram();

   for(i=0;i<64;i++){
      for(j=0;j<120;j++){
         OLED_data(0x00);
         OLED_data(0x00);
      }
      for(j=0;j<120;j++){
         OLED_data(0x00);
         OLED_data(0x00);
      }
   }
}


/******************************************************************
   Function Name:    Data_processing                               
   Return Value:                                             
   Parameters:       temp                       
   Description:      turns 1byte B/W data to 4 btyes gray data for
               display on OLED Display
******************************************************************/
void Data_processing(unsigned char temp){   

      switch(temp >> 6){                     //Comes from New Haven Display sample code. Was in Image routine.
         case 0:
            OLED_data(0x00);
            break;
         case 1:
            OLED_data(0x0F);
            break;
         case 2:
            OLED_data(0xF0);
            break;
         case 3:
            OLED_data(0xFF);
            break;
         default:
            break;
      }
         
      switch((temp >> 4) & 0x03){
         case 0:
            OLED_data(0x00);
            break;
         case 1:
            OLED_data(0x0F);
            break;
         case 2:
            OLED_data(0xF0);
            break;
         case 3:
            OLED_data(0xFF);
            break;
         default:
            break;
      }
             
      switch((temp >> 2) & 0x03){
         case 0:
            OLED_data(0x00);
            break;
         case 1:
            OLED_data(0x0F);
            break;
         case 2:
            OLED_data(0xF0);
            break;
         case 3:
            OLED_data(0xFF);
            break;
         default:
            break;
      }
         
      switch((temp) & 0x03){
         case 0:
            OLED_data(0x00);
            break;
         case 1:
            OLED_data(0x0F);
            break;
         case 2:
            OLED_data(0xF0);
            break;
         case 3:
            OLED_data(0xFF);
            break;
         default:
            break;
      }
}


/******************************************************************
   Function Name:    OLED_JIS0208                               
   Return Value:                                             
   Parameters:       character, oled_column, line                       
   Description:      Gets character data from the Multi-Font IC of the OLED Display
******************************************************************/
void OLED_JIS0208 (int character, unsigned char oled_column, char line){

   unsigned char MSB, LSB, byte1, byte2, byte3;
   unsigned char readByte, h, i, j, k, m, n;
   unsigned char colSta, colFin, rowSta, rowFin;
   unsigned char FontBuffer[32];
   unsigned char RotatedFont[4][8];
   unsigned char z = 0;                     //Make sure z always starts at 0.
   
   unsigned long Address = 0;                  //unsigned long is 32-bits
   
    //Need to split character into 2 8-bit words MSB and LSB   
   MSB = (character >> 8) & 0xFF;               //Could define each or the array as int or char or whatever
   LSB = character & 0xFF;
   
    MSB = MSB - 0x21;                        //Need to adjust to the reference of 0x0000 before the Address calculation
   LSB = LSB - 0x21;
   
   //Parameters: JIS0208 Japanese                                 
   //MSB: Higher byte of JIS code               I think we should use Japanese JIS 0208
   //LSB: Lower byte of JIS code               This will include English and the Infinity symbol
   //Address: Address of character data         Note though ALL characters will be double height 15x16 pixels
   //if((MSB >= 0x01 && MSB <= 0x5E) && (LSB >= 0x01 && LSB <= 0x5E)){
       
        Address = MSB * 0x5E;
        Address = Address + LSB;
        Address = Address * 0x20;
        Address = Address + 0x077C80;


   //To split the resulting number into bytes
   byte1 = (Address >> 16) & 0xFF;               
   byte2 = (Address >> 8) & 0xFF;               //uses bitshifting and Bitwise AND to form 8-bit values
   byte3 = Address & 0xFF;      
   
   //Send READ command and Address to Multi-Font IC.
    OLEDen = 1;
   MuFen = 0;                              //make rc5 of PIC18 go low to enable the Multi-Font IC
   MultiFont_OUT(0x0B);                      //READ command      This SPI_OUT is for the Multi-Font IC NOT the OLED
   MultiFont_OUT(byte1);                      //Address Byte1 (MSB)      
   MultiFont_OUT(byte2);                      //Address Byte2            
   MultiFont_OUT(byte3);                      //Address Byte3 (LSB)      
   MultiFont_OUT(0xFF);                      //Dummy Byte         Datasheet says 0x00, but their example has 0xFF. 0xFF is Correct.
   
   for(i = 0 ; i < 32 ; i++ ){                  //For i number of bytes to gather from Multi-Font IC.  In this case its 32.
      FontBuffer = MultiFont_IN();
   }   
   
   MuFen = 1;                              //make rc5 of PIC18 go high to disable the Multi-Font IC
   OLEDen = 0;
   
   //Need to Rotate the BitMap for each character couter-clockwise.
   //To do this we will break the 16x16 image apart into 4 8x8 bit blocks.
   //Image 0 is bytes 0-7.  Image 1 is bytes 8-15.
   //Image 2 is bytes 16-23.  Image 3 is bytes 24-31.
   for(m = 0; m < 4; m++){                     //There are 4 8x8 bit blocks to be rotated
      for(n = 0; n < 8; n++){                  //Make sure the RotatedFont array is filled with 0.
         RotatedFont[m][n] = 0;                     //m represents each image. n represents each byte in that image
      }
   }
   
   for(h = 0; h < 4; h++) {                  //There are 4 8x8 bit blocks to be rotated.
      for(k = 0; k < 8; k++) {               //There are 8 bytes per image
         for(j = 0; j < 8; j++) {            //There are 8 bits per byte
            
            if(FontBuffer[((h*8)+j)] & 1<<k){    //Use bitswise operations to break and reform the bytes
               RotatedFont[h][k] |= 1<<(7-j);       //And form it into an array
            }
            
         }
      }
   }
   
   //End of rotate functions
   
    rowSta = ((line-1)*16);                     //gives row value to lines starting with count 1.
   
   colSta = oled_column + 28;                  //28 is the 0 column reference
   colFin = colSta + 3;                     //Each character is 16 pixels wide or 2 double hex numbers.  That is 4 columns. So colSta and 3 more.
   
   rowFin = rowSta + 15;                     //Each character is 16 rows tall. So oled_row and 15 more.
   
   column(colSta, colFin);                     //Set column address from Column Start to Column Finish
   row(rowSta, rowFin);                     //Set row address from oled_row (the start row) to Row Finish
   
   wram();
   
   //Now we display the rotated image from the 2D array.
   for(i = 0; i < 8; i++){                     //We have two arrays of 8 1-byte characters each
                                    
      Data_processing(RotatedFont[0]);            //First the top half of the image
      Data_processing(RotatedFont[1]);            //We need Image0B0, Image1B0, Image0B1, Image1B1, Image0B2, ..., Image1B7
    }
   
   for(i = 0; i < 8; i++){                     //Again we have two arrays of 8 1-byte characters each
                                       //Now the bottom half of the image.
      Data_processing(RotatedFont[2]);            //We need Image2B0, Image3B0, Image2B1, Image3B1, Image2B2, ..., Image3B7
      Data_processing(RotatedFont[3]);
    }
}

   
/******************************************************************
   Function Name: display_oled_init                                         
   Return Value:  void                                         
   Parameters:    void                     
   Description:   Detects the OLED display and initializes it 
*******************************************************************/
void display_oled_init(void){

    unsigned char m;
    unsigned char q = 1;
    unsigned char n;
    unsigned short long character;
    unsigned char charcolumn;
   unsigned char num_char = 16;               //Maximum characters per line.
   
   Delay_ms(250);
   
    OLED_Init();

    int init_text[2][16] = {{0x2121, 0x2121, 0x2349, 0x236E, 0x2369, 0x2374, 0x2369, 0x2361, 0x236C, 0x2369, 0x237A, 0x2369, 0x236E, 0x2367, 0x2121, 0x2121},
                     {0x2121, 0x2350, 0x236C, 0x2365, 0x2361, 0x2373, 0x2365, 0x2124, 0x2121, 0x2377, 0x2361, 0x2369, 0x2374, 0x2125, 0x2121, 0x2121}};
                                       //Displays 'Intializing' on line 2, and 'Please, wait.' on line 3.
                                       //line 3 needs to be shifted by half a character to center under line 2.
 
   for(m = 0; m < num_char; m++){               //num_char should be changed to the exact number of characters in line_text
       
      charcolumn = (m * 4);                  //each nibble is a column, a byte is 2 columns. Each charater is 2 Bytes wide
      OLED_JIS0208(init_text[0][m], charcolumn, 2);
      charcolumn = charcolumn + 2;            //Shifts charcolumn over by 2 columns (half a character width).
      OLED_JIS0208(init_text[1][m], charcolumn, 3);      
   }

   Delay_ms(1500);
   
   Clear_OLED();                           //clears oled display
}


/******************************************************************
   Function Name:    OLED_ASCII                               
   Return Value:                                             
   Parameters:       *pAscii, colS, rowS, FontType)                     
   Description:      displays ASCII characters in one of three font types
******************************************************************/
void OLED_ASCII (unsigned char *pAscii, unsigned char colS, unsigned char rowS, unsigned char FontType){      
   
   unsigned char readByte, h, i, j, k, m, n;
   unsigned char RotatedFont[2][8];
   unsigned char FontBuffer[16];               //characters have 8 bytes of data
   unsigned long Address;                     //unsigned char is 8-bits or 1 byte, Address will be 3 bytes
   unsigned char colF, rowF, block;
   unsigned char MSB, LSB, byte1, byte2, byte3;
   
   //Parameters: Ascii code
   block = 1;
   
   Address = (*pAscii - 0x20);
   Address = Address * 8;
   colF = colS + 1;                     //All 3 Font Types are 8-bits wide. Thats 2 columns.
   
   if (FontType == 1){                     //5x7 Font
      rowF = rowS + 7;                     //Each 5x7 character covers 8x8 pixels. Thats 8 rows and 2 columns.                     
   }
   
   else if (FontType == 2){               //7x8 Font
      Address = Address + 768;
      rowF = rowS + 7;                     //Each 7x8 character covers 8x8 pixels. Thats 8 rows and 2 columns.                     
   }
   
   else if (FontType == 3){               //8x16 Font
      Address = Address * 2;
      Address = Address + 1536;
      rowF = rowS + 15;                     //Each 8x16 character covers 8x16 pixels. Thats 16 rows and 2 columns.
      block = 2;
   }
   
   
   //To split the resulting number into bytes
   byte1 = (Address >> 16) & 0xFF;               
   byte2 = (Address >> 8) & 0xFF;               //uses bitshifting and Bitwise AND to form 8-bit values
   byte3 = Address & 0xFF;   
   
   
   for(m = 0; m < 2; m++){                     //There are 4 8x8 bit blocks to be rotated
      for(n = 0; n < 8; n++){                  //Make sure the Temp array is filled with 0.
         RotatedFont[m][n] = 0;                     //m represents each image. n represents each byte in that image
      }
   }
   
   //Send READ command and Address to Multi-Font IC.
    OLEDen = 1;
   MuFen = 0;                              //make rc5 of PIC18 go low to enable the Multi-Font IC
   MultiFont_OUT(0x0B);                      //READ command      This SPI_OUT is for the Multi-Font IC NOT the OLED
   MultiFont_OUT(byte1);                      //Address Byte1 (MSB)      
   MultiFont_OUT(byte2);                      //Address Byte2            
   MultiFont_OUT(byte3);                      //Address Byte3 (LSB)      
   MultiFont_OUT(0xFF);                      //Dummy Byte         Datasheet says 0x00, but their example has 0xFF. 0xFF is Correct.
   
   if (block == 1){
      for(i = 0 ; i < 8 ; i++ ){               //For i number of bytes to gather from Multi-Font IC.  In this case its 8.
         FontBuffer = MultiFont_IN();             //read one byte From the Multi-Font IC
      }      
   }
   
   else if (block == 2){
      for(i = 0 ; i < 16 ; i++ ){               //For i number of bytes to gather from Multi-Font IC.  In this case its 8.
         FontBuffer = MultiFont_IN();            //read one byte From the Multi-Font IC
      }      
   }
   
   MuFen = 1;                           //make rc5 of PIC18 go high to disable the Multi-Font IC
   OLEDen = 0;
   
   //Need to rotate the character counter-clockwise   
   for(h = 0; h < block; h++) {                  //There are 4 8x8 bit blocks to be rotated.
      for(k = 0; k < 8; k++) {               //There are 8 bytes per image
         for(j = 0; j < 8; j++) {            //There are 8 bits per byte
            
            if(FontBuffer[((h*8)+j)] & 1<<k){    //Use bitswise operations to break and reform the bytes
               RotatedFont[h][k] |= 1<<(7-j);       //And form it into an array
            }
            
         }
      }
   }
   //End of rotate functions
         
   column(colS, colF);                     //Set column address from Column Start to Column Finish
   row(rowS, rowF);                     //Set row address from oled_row (the start row) to Row Finish
   
   wram();
   
   for(h = 0; h < block; h++) {
      for(i = 0 ; i < 8 ; i++ ){                //for the 8 bytes of font data
         Data_processing(RotatedFont[h]);       //write byte to display
      }
   }
}


/******************************************************************
   Function Name:    OLED_String                               
   Return Value:                                             
   Parameters:       *pStr, colS, rowS, FontType)                     
   Description:      displays a string of ASCII characters in one of three font types
******************************************************************/
void OLED_String(unsigned char *pStr, unsigned char col, unsigned char rowS, unsigned char FontType){

    unsigned char colS;
   
   colS = col + 28;                  //28 is the 0 column reference
    while(1)
    {
        if (*pStr == 0)
        {
            return;
        }

      OLED_ASCII (pStr, colS, rowS, FontType);
      colS += 2;
      pStr += 1;             
       
    }   
}

Title: Re: OLED Multi-Font IC Problems
Post by: Kon-L on November 10, 2017, 11:08:11 PM
As promised, here is the complete code! It is split in 2 parts due to size limitations of attachments.  Also NHD does not allow txt or c files to be attached, so I have it in pdf files.  Again I am using a PIC18F26J50 for this project with a 16MHz crystal and NHD-2.8-25664UMB3 in 4 wire serial mode(SPI).  It is compiled using Microchip's XC8 compiler.
Part 2 to follow!
Title: Re: OLED Multi-Font IC Problems
Post by: Kon-L on November 10, 2017, 11:10:08 PM
Here is Part 2!  See previous post for part 1.