Those little OLED displays with assembler?

Status
Not open for further replies.

olly_k

Member
Hey folks, so I still use assembler, don't ask why, I just like to make life difficult for myself... Somehow though I just don't like C though which doesn't help!.

Anyway, what I want to know, is there anyone here who has managed to write a routine to run the mini 64x128 OLED displays from a PIC micro, such as a 16f675 or similar?
And if so, how did you go about it? How did you generate the text, how did you manage program memory with the countless pixel data (I cannot even see how EEPROM would help here!) and are there any shortcuts that make life a little more manageable? I am clearly missing something obvious that suggests it might not be the gargantuan task I think it is, but for some reason, I am struggling to see a relatively easy way to achieve this!

If I could just reduce the steepness of the mountain in front of me I might have a go, but suspect this is where I need to learn how to compile C and use some of the many templates out there!

Thanks
 
I doubt verry much if you'll be able to do it on a 16f675. It's only got 8 pins and 1k flash. Even a 64 character set will use half the memory. However, on a larger pic there's no reason it can't be done in assembly. Do you have a link to the kind of display you're talking about?

Mike.
 
Exactly, no reason it can't be done in assembler, but you need a PIC with considerably more memory, both program memory and RAM - for a start you need a full size screen buffer in RAM, where you assemble the picture then blast it to the screen.
 
for a start you need a full size screen buffer in RAM, where you assemble the picture then blast it to the screen.
Only for full screen animations.

For general text & icons in limited locations, you can just keep overwriting the same parts of the display & clearing it on an overall change.
I've used a few graphics displays and never yet had a full image in RAM.

You do need enough overall memory for the font and any icons etc., plus adequate working ram for transferring things.
 

It's a LOT easier to have a full size buffer though, and not too big as it's only a monochrome display.
 
I've never bothered with monochrome graphics, only full colour - that probably explains the different approach!

The small OLED displays are generally just monochrome, obviously with the much larger full colour displays you don't have the space for a buffer. There's a big difference between a small display and one bit per pixel, and a large display with three bytes per pixel.
 
If I could just reduce the steepness of the mountain in front of me I might have a go, but suspect this is where I need to learn how to compile C
...
Hey folks, so I still use assembler, don't ask why, I just like to make life difficult for myself...

If you want to do this strictly in ASM, get the display, get the datasheet for the controller, get an appropriate PIC and go at it...far be it for me to try to break your stubbornness against C and your flare for making life difficult.

BUT, if one were to be amenable to learning C, at least to the point of being able to read it well enough to understand code that is out there, they could probably translate it to ASM with enough time and effort.


Of course I should qualify my response by saying that I have only used ASM with simple operations using I2C text displays.
 
I might be able to help but I will have to dig a little. I experimented with some I2C 0.91" (128x32) and 0.96" (128x64) OLED displays last year. The programs are in XC8 but could probably be converted to assembler. It will be a lot easier if you use a PIC with an MSSP module that supports I2C, otherwise you will have to bit-bang the I2C interface.



You need to use program memory for font tables. If you use a PIC that allows access to 14-bit program memory, you can squeeze a full 96 character 5x7 font table into just 240 words of memory. The font in the picture above is 10x14 and the table containing just the "0" through "9" digits uses 100 words of program memory.

Code:
/************************************************************************
 *   Packed 96 character 5x7 Font, 2.5-words-per-character, 240 words   *
 ************************************************************************/
   #asm
   PSECT myFonts, class=CODE, abs, ovrld, space=0, delta=2
     ORG 0x0F00
   Font5x7:
     dw 0x0000, 0x0000, 0x0000, 0x005F, 0x0000     ;  32 ' '  '!'
     dw 0x0007, 0x0007, 0x0014, 0x3F94, 0x3F94     ;  34 '"'  '#'
     dw 0x122A, 0x3FAA, 0x0923, 0x0988, 0x3262     ;  36 '$'  '%'
     dw 0x1B49, 0x2AA2, 0x2800, 0x0283, 0x0000     ;  38 '&'  '''
     dw 0x001C, 0x1141, 0x0000, 0x20A2, 0x0E00     ;  40 '('  ')'
     dw 0x0A08, 0x1F08, 0x0A08, 0x043E, 0x0408     ;  42 '*'  '+'
     dw 0x0050, 0x1800, 0x0008, 0x0408, 0x0408     ;  44 ','  '-'
     dw 0x0060, 0x3000, 0x0020, 0x0808, 0x0202     ;  46 '.'  '/'
     dw 0x1F51, 0x24C5, 0x1F00, 0x217F, 0x2000     ;  48 '0'  '1'
     dw 0x2161, 0x28C9, 0x2321, 0x20C5, 0x25B1     ;  50 '2'  '3'
     dw 0x0C14, 0x097F, 0x0827, 0x22C5, 0x22B9     ;  52 '4'  '5'
     dw 0x1E4A, 0x24C9, 0x1801, 0x3889, 0x0283     ;  54 '6'  '7'
     dw 0x1B49, 0x24C9, 0x1B06, 0x24C9, 0x149E     ;  56 '8'  '9'
     dw 0x0036, 0x1B00, 0x0000, 0x2B36, 0x0000     ;  58 ':'  ';'
     dw 0x0414, 0x1141, 0x0014, 0x0A14, 0x0A14     ;  60 '<'  '='
     dw 0x0041, 0x1114, 0x0402, 0x00D1, 0x0486     ;  62 '>'  '?'
     dw 0x1949, 0x3CC1, 0x1F7E, 0x0891, 0x08FE     ;  64 '@'  'A'
     dw 0x3FC9, 0x24C9, 0x1B3E, 0x20C1, 0x20A2     ;  66 'B'  'C'
     dw 0x3FC1, 0x20A2, 0x0E7F, 0x24C9, 0x24C1     ;  68 'D'  'E'
     dw 0x3F89, 0x0489, 0x00BE, 0x20C9, 0x24FA     ;  70 'F'  'G'
     dw 0x3F88, 0x0408, 0x3F80, 0x20FF, 0x2080     ;  72 'H'  'I'
     dw 0x1040, 0x20BF, 0x00FF, 0x0414, 0x1141     ;  74 'J'  'K'
     dw 0x3FC0, 0x2040, 0x207F, 0x010C, 0x017F     ;  76 'L'  'M'
     dw 0x3F84, 0x0410, 0x3FBE, 0x20C1, 0x20BE     ;  78 'N'  'O'
     dw 0x3F89, 0x0489, 0x033E, 0x20D1, 0x10DE     ;  80 'P'  'Q'
     dw 0x3F89, 0x0CA9, 0x2346, 0x24C9, 0x24B1     ;  82 'R'  'S'
     dw 0x0081, 0x3F81, 0x00BF, 0x2040, 0x203F     ;  84 'T'  'U'
     dw 0x0FA0, 0x2020, 0x0FBF, 0x2038, 0x203F     ;  86 'V'  'W'
     dw 0x3194, 0x0414, 0x3187, 0x0470, 0x0407     ;  88 'X'  'Y'
     dw 0x30D1, 0x24C5, 0x2180, 0x3FC1, 0x2080     ;  90 'Z'  '['
     dw 0x0104, 0x0410, 0x1000, 0x20C1, 0x3F80     ;  92 '\'  ']'
     dw 0x0202, 0x0082, 0x0240, 0x2040, 0x2040     ;  94 '^'  '_'
     dw 0x0001, 0x0104, 0x0020, 0x2A54, 0x2A78     ;  96 '`'  'a'
     dw 0x3FC8, 0x2244, 0x1C38, 0x2244, 0x2220     ;  98 'b'  'c'
     dw 0x1C44, 0x2248, 0x3FB8, 0x2A54, 0x2A18     ; 100 'd'  'e'
     dw 0x047E, 0x0481, 0x010C, 0x2952, 0x293E     ; 102 'f'  'g'
     dw 0x3F88, 0x0204, 0x3C00, 0x227D, 0x2000     ; 104 'h'  'i'
     dw 0x1040, 0x223D, 0x007F, 0x0828, 0x2200     ; 106 'j'  'k'
     dw 0x0041, 0x3FC0, 0x007C, 0x0218, 0x0278     ; 108 'l'  'm'
     dw 0x3E08, 0x0204, 0x3C38, 0x2244, 0x2238     ; 110 'n'  'o'
     dw 0x3E14, 0x0A14, 0x0408, 0x0A14, 0x0C7C     ; 112 'p'  'q'
     dw 0x3E08, 0x0204, 0x0448, 0x2A54, 0x2A20     ; 114 'r'  's'
     dw 0x023F, 0x2240, 0x103C, 0x2040, 0x107C     ; 116 't'  'u'
     dw 0x0E20, 0x2020, 0x0E3C, 0x2030, 0x203C     ; 118 'v'  'w'
     dw 0x2228, 0x0828, 0x220C, 0x2850, 0x283C     ; 120 'x'  'y'
     dw 0x2264, 0x2A4C, 0x2200, 0x0436, 0x2080     ; 122 'z'  '{'
     dw 0x0000, 0x3F80, 0x0000, 0x20B6, 0x0400     ; 124 '|'  '}'
     dw 0x0808, 0x0410, 0x0478, 0x2341, 0x2378     ; 126 '~'  ''
   #endasm
And here is the routine to unpack a compressed character (target = 12F1840. notice the drivers are mostly assembly language);
Code:
/************************************************************************
 *                                                                      *
 *                                  *************************************/
   void rdflash()                   //
   { asm("movlb  EECON1/128     "); // bank 3                         |03
     asm("bcf    EECON1,6       "); // CFGS = 0 (not config)          |03
     asm("bsf    EECON1,7       "); // EEPGD = 1 (program memory)     |03
     asm("bsf    EECON1,0       "); // RD = 1 (initiate read)         |03
     asm("nop                   "); // required nop                   |03
     asm("nop                   "); //  "                             |03
     asm("incf   EEADRL,F       "); // bump EEADR                     |03
     asm("rlf    EEDATL,W       "); // move b7 into Carry             |03
     asm("rlf    EEDATH,W       "); // wreg = the 7 bit hi byte       |03
     asm("bcf    EEDATL,7       "); // eedatl = the 7 bit lo byte     |03
   }                                //

/************************************************************************
 *                                                                      *
 *                                  *************************************/
   void OLED_char(char ascii)       // send ASCII 5x7 character font
   { static char ndx;               //
     asm("addlw  -32            "); // ascii 32..127 minus offset     |02
 //  asm("banksel putascii@ndx  "); // bank 0 ?                       |00
     asm("movwf  OLED_char@ndx  "); // save table index, 0..95        |02
     asm("lsrf   WREG,F         "); // int(index *= 2.5) -> 0..237    |02
     asm("addwf  OLED_char@ndx,W"); //  "                             |02
     asm("addwf  OLED_char@ndx,W"); //  "                             |02
     asm("movlb  EEADRL/128     "); // bank 3                         |03
     asm("movwf  EEADRL         "); //                                |03
     asm("movlw  Font5x7/256    "); //                                |03
     asm("movwf  EEADRH         "); // flash address hi               |03
/*                                                                      *
 *   Extract five bytes of font data from three words of memory. An     *
 *   even character uses word 0 hi + lo, 1 hi + lo, and 2 hi while      *
 *   odd characters use word 0 lo, 1 hi + lo, and 2 hi + lo.  A 6th     *
 *   blank (zero) byte is sent for inter-character spacing.             *
 *                                                                      */
     i2c_start(0x78);               // send I2C 'start'
  // i2c_write(0x78);               // send I2C 'address'
     i2c_write(0x40);               // send SSD 'control' Co=0 D/C=1
     asm("call   _rdflash       "); // read 1st word (2 bytes)        |03
 //  asm("banksel wrchar@ndx    "); // bank 0                         |00
     asm("btfss  OLED_char@ndx,0"); // odd char? yes, skip, else      |03
     asm("call   _i2c_write     "); // send hi byte (even character)  |02
     asm("movlb  EEDATL/128     "); // bank 3                         |03
     asm("movf   EEDATL,W       "); //                                |03
     asm("call   _i2c_write     "); // send lo byte                   |02
     asm("call   _rdflash       "); // read 2nd word (2 bytes)        |03
     asm("call   _i2c_write     "); // send hi byte                   |02
     asm("movlb  EEDATL/128     "); // bank 3                         |03
     asm("movf   EEDATL,W       "); //                                |03
     asm("call   _i2c_write     "); // send lo byte                   |02
     asm("call   _rdflash       "); // read 3rd word (2 bytes)        |03
     asm("call   _i2c_write     "); // send hi byte                   |02
     asm("movlb  EEDATL/128     "); // bank 3                         |03
     asm("movf   EEDATL,W       "); //                                |03
 //  asm("banksel wrchar@ndx    "); // bank 0                         |00
     asm("btfsc  OLED_char@ndx,0"); // even char? yes, skip, else     |03
     asm("call   _i2c_write     "); // send lo byte (odd characters)  |02
     asm("movlw  0              "); //                                |02
     asm("call   _i2c_write     "); // send blank 6th column          |02
     i2c_stop();                    //
   }                                //
The code can get a bit involved but don't let that scare you off... which reminds me... compared to examples found in Adafruit libraries for Arduino, you can use a much abbreviated OLED init command sequence (9 or 11 bytes, depending on screen orientation) for the 0.96" display. Here's what I send;
Code:
/************************************************************************
 *   init command sequence for 0.96" 128x64 SSD1306 OLED display        *
 ************************************************************************/

   const unsigned char ssdcfg[] = { 
  // 0xAE,                          // displayoff . . . . . . . (default)
  // 0xD5, 0x80,                    // displayclockdiv & param  (default)
  // 0xA8, 0x3F,                    // setmultiplex & param ()  (default)
  // 0xD3, 0x00,                    // setdisplayoffset & param (default)
  // 0x40,                          // setstartline . . . . . . (default)
     0x8D, 0x14,                    // chargepump & param
     0x20, 0x00,                    // memorymode 'horizontal'
  // 0xA1,                          // segremap (A0/A1, flip horizontally)
  // 0xC8,                          // comscandec (C0/C8, flip vertically)
  // 0xDA, 0x12,                    // setcompins () . . . . .  (default)
  // 0x81, 0x7F,                    // setcontrast, 0-255 . . . (default)
     0xD9, 0xF1,                    // setprecharge
     0xDB, 0x40,                    // setvcomdetect
  // 0xA4,                          // displayallon_resume . .  (default)
  // 0xA6,                          // normaldisplay . . . . .  (default)
     0xAF                           // displayon
   };                               //

Cheerful regards, Mike, K8LH
 
Last edited:
By the way, guys. The font tables for these 0.91" and 0.96" OLED displays are the same ones you would use for a Nokia 5110 display...

The little XC8 test program for the 12F1840 contains a 10 character 10x14 font table at 0x0E00 and a 96 character 5x7 font table at 0x0F00. Total memory used = 743 words.

I've attached the program below. The caveat is this was a learning exercise and the comments may be quite cryptic and sparce. Also, even though I've been playing with PICs for many years, this was the first time I did anything with I2C so there may be some errors in those drivers...

Cheers...
 

Attachments

  • 12F1840_I2C_v1(128x64).c
    26.1 KB · Views: 388
Last edited:
Where are they, please (XC8, not CCS)? I would love to study them.

TIA... Mike

Ok, Library was a big word but - I was referring to the various C-based code for Nokia 5110 displays available on Arduino - one can easily port over to XC8.
I have done this for a proof of concept for a little clock. No big deal. It looks very much like your font table snippets above.
 
Ok, Library was a big word but - I was referring to the various C-based code for Nokia 5110 displays available on Arduino - one can easily port over to XC8.

Except the libraries are C++ not just C, so less trivial to convert to XC8 than it might have been. I've done a considerable amount of such porting
 
Except the libraries are C++ not just C, so less trivial to convert to XC8 than it might have been. I've done a considerable amount of such porting


Agreed, but since I am far from an expert and I was able to make a nokia 5110 work in less than an hour, I put the process in the "fairly easy" category.
 
Thanks for the help folks. Mike that's some good work there I will study it! And yes, I keep saying I will give C a go but keep shying away! Silly really!
 
I had a play with the .96" type some time ago. was in C , ( not very good C )
 
Hey, OLLY_K. Still interested in an assembly language 12F675 OLED display demo? May I ask which OLED module(s) you have, please? Are they the little 0.91" and/or 0.96" units with an SSD1306 controller? If so, do you have a copy of the SSD1306 datasheet?

Please let us know and we can help you figure out all the pieces needed to build an app'...

Cheerful regards, Mike, K8LH

 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…