SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By robodude666
#58289
Hey guys,

A year or two ago when the OLED module came out I started playing around with it. I managed to get it to work via SPI on a PIC18F4550. Since then I was busy and didn't have much time to play with it. Recently I found some spare time so I've decided to get it connected to my Arduino.

I managed to get SPI to work on the Arduino, but I'm having trouble getting Parallel to work. I based my code mostly off of the OLED_example.c that sparkfun provides, however I've had no luck.

I triple checked my pins and everything is correct. My SPI code works perfectly fine, but when I try doing parallel the screen just stays black. I tried both 6800 and 8800 parallel by changing the BS1 and BS2 jumpers around (6800 is BS1 ground, BS2 to vcc and 8800 is both to vcc). I try my code with both as I don't know which SparkFun used for their example.

I am powering the OLED by an external 3v battery (2x AA, as the arduino's 3.3v outputs 4.0v for me which is out of the OLED spec).

Do I need to do anything with the "EN" pin? I have the original v01 board but the schematic on the SF website is v22 so I don't know whats what.

Below is the Arduino code. Ignore the colors as I use them to determine which wire is where. I originally used D0-7 and manually set them, but I moved everything over to Arduino's 0-7 pins which is connected to PORTD. I'm setting PORTD directly now instead of manually setting all the bits. I'll change to to a for loop + array after I get it working. Sorry for the messy code. I'll clean it up and make it pretty after it works.
Code: Select all
// zomg pins
int RES = 8; //blue 
int RW  = 1; //green ANALOG 
int RD  = 9; //yellow  
int CS  = 0; //blue ANALOG 
int DC  = 10;//red    

// Data pin defines
// NOTE: replace with array later
int D0 = 0;  // blue
int D1 = 1;  // green
int D2 = 2;  // yellow
int D3 = 3;  // red
int D4 = 4; // blue
int D5 = 5; // green
int D6 = 6; // yellow
int D7 = 7; // red


/* Sparkfun based code */

//init
void sparkFun_init();

//reset
void Reset_SSD1339();

// write command or data
void write_c(byte out_command);
void write_d(byte out_data);

//Based on SparkFun functions for testing purposes
void write_c(byte out_command)
{
  digitalWrite(DC, LOW);
  analogWrite(CS, LOW);
  analogWrite(RW, LOW);

  PORTD = out_command;
  delayMicroseconds(120);
  
  analogWrite(RW, HIGH);
  analogWrite(CS, HIGH);
  digitalWrite(DC, HIGH);
}

void write_d(byte out_data)
{
  digitalWrite(DC, HIGH);
  analogWrite(CS, LOW);
  analogWrite(RW, LOW);

  PORTD = out_data;
  delayMicroseconds(120);

  analogWrite(RW, HIGH);
  analogWrite(CS, HIGH);
  digitalWrite(DC, HIGH);
}

void Reset_SSD1339()
{
    digitalWrite(RES, LOW);
    delay(100);
    digitalWrite(RES, HIGH);
}

void sparkFun_init(){
  
  digitalWrite(RD, HIGH);
  digitalWrite(DC, LOW);
  analogWrite(RW, LOW);
  analogWrite(CS, LOW);
  
  Reset_SSD1339();
  
  write_c(0xa0); // Set Re-map / Color Depth
  write_d(0x34);//0xb4); // 262K 8bit R->G->B <-- 0b0011.0100
  
  write_c(0xa1); // Set display start line
  write_d(0x00); // 00h start
  
  write_c(0xA6); // Normal display
  
  write_c(0xad); // Set Master Configuration
  write_d(0x8e); // DC-DC off & external VcomH voltage & external pre-charge voltage
  
  write_c(0xb0); // Power saving mode
  write_d(0x05);
  
  write_c(0xb1); // Set pre & dis_charge
  write_d(0x11); // pre=1h dis=1h
  
  write_c(0xb3); // clock & frequency
  write_d(0xf0); // clock=Divser+1 frequency=fh
  
  write_c(0xbb); // Set pre-charge voltage of color A B C
  write_d(0x1c); // color A
  write_d(0x1c); // color B
  write_d(0x1c); // color C
  
  write_c(0xbe); // Set VcomH
  write_d(0x1f); //
  
  write_c(0xc1); // Set contrast current for A B C
  write_d(0xaa); // Color A
  write_d(0xb4); // Color B
  write_d(0xc8); // Color C
  
  write_c(0xc7); // Set master contrast
  write_d(0x0f); // no change
  
  write_c(0xca); // Duty
  write_d(0x7f); // 127+1
  
  write_c(0xaf); // Display on
}

void setup(){
  
  int i, x, y;
  
  pinMode(DC, OUTPUT);
  pinMode(CS, OUTPUT);
  pinMode(RES, OUTPUT);
  pinMode(RW, OUTPUT);
  pinMode(RD, OUTPUT);
  
  //replace with array
  pinMode(D0, OUTPUT);
  pinMode(D1, OUTPUT);
  pinMode(D2, OUTPUT);
  pinMode(D3, OUTPUT);
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(D7, OUTPUT);
  
  PORTD = 0x00;
  
  sparkFun_init();
  
  delay(120);

  write_c(0x8e);    // clear window command
  write_d(0);
  write_d(0);
  write_d(130);
  write_d(130);

  delay(100);
  
  write_c(0x92);    // fill enable command
  write_d(0x01); 
  
  delay(10);
  
  randomSeed(analogRead(5));
 
  // draw 100 random dots
  for(i = 0; i < 100; i++)
  {
      x = random(0, 129);
      y = random(0, 129);
      
      write_c(0x83);
      write_d(x);
      write_d(y);
      write_d(x+1);
      write_d(y+1);
      write_d(128);
      write_d(128);
  } 
}

void loop(){
  //not looping 
}
Any help would be appreciated, thanks!

-robodude666
By robodude666
#58297
Hey guys,

It appears that changing the analog pins to digital solved the problem. The parallel code also works in 8800 mode which is both BS1 and BS2 on Vcc. It draws far faster now, but I'm now having some timing issues I think. It randomly draws stuff I didn't ask and sometimes randomly resets and starts the program over. Any ideas?

-robodude666
By CommanderBob
#58304
Glad you got it to work!

As for the timing if you try to draw something before the display is done executing your previous command it will glitch up and draw random stuff. At least that is what I experienced. Try throwing a delay between commands and see if that fixes it.

If your AVR is resetting I don't know what would cause that except a code problem or a brownout. I had a problem with AVRGCC on Ubuntu where I could not use some string functions without it compiling bad code where the AVR would reset every time it got to that section. The newer AVRGCC works I believe.

Also make sure you don't tell it to draw lines that it can't. See the datasheet. I think it is lines with a negative slope, maybe positive.

Justin
By Blackfin
#58349
CommanderBob wrote:Glad you got it to work!

As for the timing if you try to draw something before the display is done executing your previous command it will glitch up and draw random stuff. At least that is what I experienced. Try throwing a delay between commands and see if that fixes it.
The SSD1339 does seem to have a busy flag though the documentation doesn't talk about it. If you read the status register (read with D/C low), check bit 2 (0x04): when high, the SSD1339 is busy. I've tried it and it seems to work fine. I check the busy flag after most operations except writing to the display RAM which doesn't seem to require much time to complete.
Also make sure you don't tell it to draw lines that it can't. See the datasheet. I think it is lines with a negative slope, maybe positive.

Justin
This is what I've found and I hope someone can confirm it. The datasheet indicates that when drawing a line from (x1,y1) to (x2,y2):

x1<x2<131
y1<y2<131

I believe I've drawn lines where both end points are less than both start points but there are definitely quadrants I can't draw lines in using the GAC command.

Trying to draw lines with "illegal" end points will draw lines on the screen though they don't appear where they're supposed to...
By robodude666
#58355
Thanks for the replies guys!

Do you have a little example of how to read the status register? Do I set RW high and RD to low, set DC to low and then read the D2 pin? If D2 is high, then the SSD1339 is busy? Or would I need to write something to it first to tell it to tell me if it is busy or not?

I rewrote the above code to support Kam's command/data buffer. I could add a status checker after the buffer is full and delay the write of new data until the SSD1339 isn't busy.

I checked all of my individual writing functions and they all seem to work properly. I can draw a single character (using a fonts array) to the screen, but when it comes to parsing a char array (aka string) it starts to do funky things. Adding 100ms delays between pixel writes seems to smooth things out but causes a 3.5 second write time for a single character.

How does writing to the RAM work? Does it write each pixel horizontally and overflows fall onto the next line? Or is there a way to write to a specific spot via the RAM command?

As for illegal points, I have a number of draws where X1 is equal to X2 and Y1 is equal to Y2 and they all draw perfectly fine. I seem to get problems when I tell it to draw stuff very quickly (for example, a nested for loop to draw X and Y dots).

EDIT:

I went back and read the documentation for the SSD1339 more carefully. They mention about the write/read to ram, as well as the status register, but I am terrible at reading the timing diagrams. I basically understand that you need to set DC low and RW high to read the status, but I don't understand where the status is read from.

As for writing, it goes in the order of how you set it up in the re-map. For me, it goes horizontally across the screen and then up to the next row as I set it to "0" in my config. How does it get the color to draw? Do you send it 3 bytes of data, each with the A, B and C colors? Or whatever order you set it to? Speaking of which, what is A B and C? I understand that is the color, but it doesn't describe how to convert RGB to ABC.

EDIT:

DING! I started from the top of the documentation and read it all over again. I forgot that we're in a 262k color world and not 16.7million world so it isn't 0-255! It is 0-63 ;). Table 8-9 shows that bits 7 and 6 are ignored because we're only using 6 bits to get the 262 colors. With that set, yes. You send the RAM command and then the ABC or CBA colors based on your bit 2 definition in the remap. Since I have it set to 0 I do ABC which seems to be the same as BGR. Sending it 63, 63, 0 draws a tealish blue pixel which is the similar to #00FFFF in our 16.7million world.

THANKS! :D.

I'm going to write a little class which will take a 16.7mill R, G and B value, find the closest 262k value for it, and output the two color bytes I need to use :).

-robodude666
By Blackfin
#58369
My project is done on a Freescale 9S08GB60 microcontroller in assembler but it should be fairly clear what I'm doing here to read the status:
Code: Select all
;***************************************************************************
;* Function   	: OLED_ReadStatus
;* Description	: 
;* Variables	:
;* Subroutines	:
;*
;***************************************************************************
OLED_ReadStatus:
        mov     #$00,OLED_DATA_PORTDD   ;set as input
        bclr    OLED_DC,OLED_CTL_PORT   ;set for data, clr for status
        bclr    OLED_CS,OLED_CTL_PORT   ;assert CS
        bclr    OLED_RD,OLED_CTL_PORT   ;set RD low
        nop
        nop
        nop
        nop
        lda     OLED_DATA_PORT          ;read the data into AccA
        bset    OLED_RD,OLED_CTL_PORT   ;release RD
        bset    OLED_CS,OLED_CTL_PORT   ;release CS
        
        rts
and my check-busy looks like this:
Code: Select all
;***************************************************************************
;* Function   	: OLED_WaitBusy
;* Description	: 
;* Variables	:
;* Subroutines	:
;*
;***************************************************************************
OLED_WaitBusy:
        jsr     OLED_ReadStatus
        and     #OLED_BUSY_FLAG
        
        bne     OLED_WaitBusy
        
        rts
With this I don't have to add any delay loops after time-consuming commands.

I've got my OLED set-up for just 256 colours which is one byte per pixel. Even 256 colours gives very good looking images. A 256-colour full-screen bitmap could still be sizeable at 16Kb (sizeable by the native memory size in my microcontroller, that is...)

I created a little app to take a 24-bit BMP and create a RLE-encoded .txt file for colour images, RLE to save space. I made up a second app to create a slightly different RLE encoding for small, mono bitmaps like digits and characters.

Try using a GAC command to draw some lines on the screen and let me know how that goes...
By robodude666
#58370
How does 256 color mode work? Color A has only 2 bits while color B and C have 3 bits? So color A is less precise?

Huh, I hate reading assembly. Lets see..

Set D0-7 to 0x00 (doesn't that kill the command or datayou just sent?) then set DC, CS and RD to low. Nop (hehe) a few times to wait for a reply and then read D0-7 then set RD and CS back to high? Then if D2 is high you wait until it goes low and then spit out more commands at it.

And WaitBusy is basically:

while(ReadStatus() == HIGH);

Do nothing until the SSD1339 is still busy.

RLE, hm. I'll consider that as an option as I plan to use a microSD or EEPROM to store the images. How did you parse the BMP file? I've searched on the googles for methods but can't seem to find anything. All I know is PHP has a imagecolorat function which gets the color at a specific pixel... But PHP is slow compared to other languages.
By Blackfin
#58378
robodude666 wrote:How does 256 color mode work? Color A has only 2 bits while color B and C have 3 bits? So color A is less precise?
Yes, in 256 colour mode the red has 4 levels of brightness (2 bits) while each of the other two colours, blue and green, have 8 levels (3 bits each). In certain images I've tried, it's quite obvious but in others the image looked fine. Of course, if accuracy is your thing and you've got the memory for 18 bits per pixel, make use of them. For my project, a gauge face and simple logo, 256 was fine.

Huh, I hate reading assembly. Lets see..

Set D0-7 to 0x00 (doesn't that kill the command or datayou just sent?)
That first line sets the data direction register for the port so the pins act as inputs. My setup is running an Fbus of about 18.87MHz and a NOP is one cycle (53nS) so I just wanted to add a couple hundred nS of delay for PWcsl between RD going low and reading the data at the port per Fig 13-2. I could probably reduce it to 3 NOPs from four and be safe to speed things up a smidge I guess.
then set DC, CS and RD to low. Nop (hehe) a few times to wait for a reply and then read D0-7 then set RD and CS back to high? Then if D2 is high you wait until it goes low and then spit out more commands at it.

And WaitBusy is basically:

while(ReadStatus() == HIGH);
Essentially. ReadStatus() is a function because it can also return a couple of other bits of status (see table 9-3.) So I check that the bit indicating busy in the return value from ReadStatus() is high:

while( ReadStatus() & 0x04 );
RLE, hm. I'll consider that as an option as I plan to use a microSD or EEPROM to store the images.
My "custom" RLE was fairly simple. For colour images, I'd have two bytes, the first is the number of repeats and the second is the byte value to write. If a run of more than 255 bytes is needed for a colour, I simply write $ff,$xx (xx=byte to write) and start another pair for the same colour. For highly complex pictures, you might not get much compression. But for logos and images on a solid colour background, it works nicely.

For smaller mono bitmaps like characters, I'd encode a single byte with the info. The top bit would indicate if the pixels are "on" or "off" with the current colour and the lower 7 bits would encode how many bits of that type to send. This allows up to 127 pixels to be set with a single byte of memory data.

These methods mesh nicely with a dedicated write routine that sets the data bus to the appropriate colour value and simply toggles the WR line the number of times in the run.
How did you parse the BMP file? I've searched on the googles for methods but can't seem to find anything.
There's lots of info on BMPs available. Search "BMP file format" and you'll get some results. The nice thing about 24bpp images is that once you get past the header information in the file you're given pixel map that's simply 3 bytes per pixel, blue, green red, in that order. 24bpp images don't have RGB quads in a colour palette to muck around with, the value in the three bytes defines the intensity of the colours directly. In fact, the BMP image is stored in a handy way for me too with the first entries in the image corresponding to the bottom left of the image, just how I draw it on the screen.

So for converting to 256 colours, I'd take the red value for a pixel and divide 0-255 into 4 "sections", each 64-counts wide. So for the two red bits in the single 256-colour byte, a red value in the BMP of 0-63 gives 0bxxxxxx00. 64-127 gives 0bxxxxxx01. 128-191 gives 0bxxxxxx10 and anything 192 or above gets 0bxxxxxx11. For the other two colours, simply divide their ranges into 8 sections since they have 3 bits each. During this conversion you can also use RLE encoding if you like -- and if your image suits it -- to save some space in flash.
By robodude666
#58420
Awesome. I'll look into RLE and parsing bitmaps another day. Right now I'm more worried about getting the OLED to render stuff properly and quickly.

I changed the last two bits in the remap to enable 256 color mode and played around with it. I don't honestly see any gain in using 256 color instead of 256k. In order to write to the ram I still need to use 3 bytes of data, and because its 256 colors each color only uses up 2-3 bits of the 8 bits. The other 5-6 bits is repeated info. Also, to use colors when drawing a line using the accelerated functions I still need to send 2 bytes for CCCCCBBB and BBBAAAAA. In figure 8-13 the documentation mentions that you can send 256 color data in 8-bit as CCCBBBAA, but it doesn't mention where it is applicable. The rest of the documentation refers to colors as 2 bytes: CCCCCBBB and BBBAAAAAA. Which, actually, is the 65k color depth.

Or am I missing something here?
By Blackfin
#58422
robodude666 wrote:I changed the last two bits in the remap to enable 256 color mode and played around with it. I don't honestly see any gain in using 256 color instead of 256k. In order to write to the ram I still need to use 3 bytes of data, and because its 256 colors each color only uses up 2-3 bits of the 8 bits. The other 5-6 bits is repeated info. Also, to use colors when drawing a line using the accelerated functions I still need to send 2 bytes for CCCCCBBB and BBBAAAAA. In figure 8-13 the documentation mentions that you can send 256 color data in 8-bit as CCCBBBAA, but it doesn't mention where it is applicable. The rest of the documentation refers to colors as 2 bytes: CCCCCBBB and BBBAAAAAA. Which, actually, is the 65k color depth.

Or am I missing something here?
In 256 colour mode, one pixel is represented by one byte, not three. The encoding is:

bit 7 blue 2
bit 6 blue 1
bit 5 blue 0
bit 4 green 2
bit 3 green 1
bit 2 green 0
bit 1 red 1
bit 0 red 0

(See section 8.7.3 of the datasheet) So one write, one byte, sets all three colours for a given pixel. Seeing as how you can write the entire 128x128 array with 1/3rd the number of writes (1 byte per pixel instead of 3 bytes per pixel), you should see a performance increase.

The GAC stuff is strange, I'll agree. For this, I would take the 256-colour byte above and create the necessary bytes to follow the GAC protocol, using the RGB information in my colour byte.

For example, the line draw command calls for two colour bytes:

B1: BBBBBGGG
B2: GGGRRRRR

B=blue, G=green, R=red

giving 5 bits for blue (0-31), 6 bits for green (0-64) and 5 bits for red (0-31).

The 256 colour mode only gives 3 bits for blue and green (0-7) and 2 bits for red (0-3). So similar to how one can take a 24bpp BMP and section a byte into 8 parts to get a best fit for a 3-bit colour encoding (or 4 parts for a 2-bit colour encoding), we can do the same here.

In the GAC command, blue allows values from 0-31 but we can only give a value from 0-7 with three bits. If we shift left 2 bits, we lose two LSBs of colour intensity but gain more dynamic range. So for each of green and blue, we encode the three upper bits of their respective bytes with our colour information. For the red, we encode only the upper two bits since we only have 2 bits to work with.

If the colour byte in is 0b11001110 then:

blue = 110
green = 011
red = 10

So for the line draw GAC command colour bytes:

B1: BBBBBGGG
B2: GGGRRRRR

we can encode:

B1: 11000011
B2: 00010000

That is:

blue: BBBBBB = 110xx (where x=0)
green: GGGGGG is 011xxx (x=0)
red: RRRRR = 10xxx (x=0)

Writing these bytes to the SSD1339 during a GAC command should produce the colours you want, as closely as 256-colours can get.

I don't know why the GAC doesn't adjust for different colour depths. It definitely requires the bytes to be sent as above.
By robodude666
#58425
I understand all of that. I also understand how to write the 256 colors to fit the GAC's 2 byte requirement.

What I don't understand is Figure 8-14 v Figure 8-13.

Figure 8-13 describes what you describe that 256 color depth is represented by 1 byte of: 0bCCCB.BBAA, however in Figure 8-14 it says that writing data to RAM requires 3 bytes. Each byte is a single color and that color A uses 2 bits so bits 0-3 are filled with bit 5s value. Same goes for color C, and color B is the same except it is 3 bits and only bits 0-2 are empty. In all cases bit 6 and 7 are 0.

I tried do write to RAM directly like before but only sending 1 byte of color per Figure 8-13 specs and I got nothing on the screen. Doing what Figure 8-14 said got me the colors I told it to draw.


As for the OLED freaking out when drawing text, I think I figured it out... and has something to do with passing the char pointer to my displayText function which uses a for loop to go through all characters until it hits a null at the end. I noticed that it works perfectly with a 7 character string (like "HELOBOB" but as soon as I go over the 7 char size it freaks out. I do not understand why this happens as I can write each of the characters manually without any problems.

I am prototyping the function as:

void displayText(char *charString, unsigned int scale, unsigned int startX, unsigned int startY);

and use:

for(int i = 0; charString; i++){
//..
}

to go through the each character of the string.

I also tried prototyping with char charString[] but get the same results.

-robodude666
By Blackfin
#58427
robodude666 wrote:I understand all of that.
My apologies.
What I don't understand is Figure 8-14 v Figure 8-13.
8-13 shows what you actually write to the databus. Figure 8-14, as I understand it, is how the SSD1339 internally stores the colour information resulting from that one write. I didn't pay any attention to 8-14 to be honest as it dealt with internal SSD operations that didn't affect what I was doing.
I tried do write to RAM directly like before but only sending 1 byte of color per Figure 8-13 specs and I got nothing on the screen. Doing what Figure 8-14 said got me the colors I told it to draw.
Here's my OLED initialization routine showing the data bytes I send for each command. Try comparing my initialization values to yours (even though its in 9S08 assembler, the gist of what I'm doing is clear...):
Code: Select all
;***************************************************************************
;* Function   	: InitializeOLED
;* Description	: 
;* Variables	:
;* Subroutines	:
;*
;***************************************************************************
InitializeOLED
        jsr     OLED_Reset
        
        ;remap colour depth
        lda     #SET_REMAP_COLORDEPTH
        jsr     OLED_WriteCommand
        lda     #$20
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;set display start line
        lda     #SET_DISPLAY_START_LINE
        jsr     OLED_WriteCommand
        lda     #$00
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;set the display offset
        lda     #SET_DISPLAY_OFFSET
        jsr     OLED_WriteCommand
        lda     #$00
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;set normal display
        lda     #SET_DISPLAY_MODE_NORMAL
        jsr     OLED_WriteCommand
        jsr     OLED_WaitBusy
        
        ;set master config
        lda     #SET_MASTER_CONFIGURATION
        jsr     OLED_WriteCommand
        lda     #$8f
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;set powersave mode
        lda     #SET_POWERSAVE_MODE
        jsr     OLED_WriteCommand
        lda     #$00
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;precharge period
        lda     #SET_RST_PRECHARGE_PERIOD
        jsr     OLED_WriteCommand
        lda     #$74
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;front clock divider
        lda     #SET_FRONTCLOCK_DIVIDER
        jsr     OLED_WriteCommand
        lda     #$F0
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ResetCOPWD
        
        ;precharge volts
        lda     #SET_PRECHARGE_VLTS_ABC
        jsr     OLED_WriteCommand
        lda     #$1C
        jsr     OLED_WriteData
        lda     #$1C
        jsr     OLED_WriteData
        lda     #$1C
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;set VCOMH
        lda     #SET_VCOMH
        jsr     OLED_WriteCommand
        lda     #$1F
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;per-colour contrast
        lda     #SET_CONTRAST_CURRENT_ABC
        jsr     OLED_WriteCommand
        lda     #$80
        jsr     OLED_WriteData
        lda     #$80
        jsr     OLED_WriteData
        lda     #$80
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ResetCOPWD
        
        ;master contrast
        lda     #SET_CONTRAST_CURRENT_MSTR
        jsr     OLED_WriteCommand
        lda     #$0F
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;set mux ratio
        lda     #SET_MUX_RATIO
        jsr     OLED_WriteCommand
        lda     #128
        jsr     OLED_WriteData
        jsr     OLED_WaitBusy
        
        ;set sleep mode off
        lda     #SET_SLEEP_MODE_OFF
        jsr     OLED_WriteCommand
        jsr     OLED_WaitBusy
        
        ResetCOPWD
        
        jsr     OLED_ResetRegion        
        
        lda     #0
        psha
        lda     #0
        psha
        lda     #127
        psha
        lda     #127
        psha
        jsr     OLED_ClearWindow
        ais     #4
        
        rts
The above works fine for me in 256-colour mode. (0,0) is at the bottom left of the screen (connector pointing down); cols increment right, rows increment up.
As for the OLED freaking out when drawing text, I think I figured it out... and has something to do with passing the char pointer to my displayText function which uses a for loop to go through all characters until it hits a null at the end. I noticed that it works perfectly with a 7 character string (like "HELOBOB" but as soon as I go over the 7 char size it freaks out. I do not understand why this happens as I can write each of the characters manually without any problems.

I am prototyping the function as:

void displayText(char *charString, unsigned int scale, unsigned int startX, unsigned int startY);

and use:

for(int i = 0; charString; i++){
//..
}

to go through the each character of the string.

I also tried prototyping with char charString[] but get the same results.

-robodude666


I don't know anything about the Arduino but a couple of things come to mind. Does the processor have RAM paging and if so, is it possible the location of the string crosses a page boundary at the 8th character position? Is there a COP watchdog that needs kicking within or before the loop? (maybe the 8th character just takes that little extra time...) If you've got a busy flag check, how long is it taking? For RAM writes, it should be instantaneous I'd think so if it's taking more time than this, check your status read.

It certainly doesn't sound like an issue related to the OLED itself.
By robodude666
#58430
Hey,

No problem. I should of been more clear.

I'll try writing to the RAM in 256 color more in 1 byte again. Maybe I screwed up somewhere.

As for the text, it goes slow. Very slow. If I write something 7 characters or under it shows up on the screen in a blink of an eye. However, if I try to print something that is 9 characters long like: "ZOGCASssX" it goes very slow. It shows a single character instantly, but then there is a second delay in between characters. If I send 10 characters, like "ZOGCASshsX" then it renders the first two characters and freaks out, similar to how old tube TVs went fuzzy when losing a signal. It might be a problem with the Arduino, not sure. I'll check the Arduino forums if anyone had similar problems.

Could it be a problem with the string not fitting into the char pointer/array properly? Should I try like having a 12-character global char array that I fill and read from rather than passing the string? I think there is something funky going on Arduino related as using the "scale" variable causes the display to go blank.

EDIT:

heh, you're going to kill me. It turns out I made a copy of my remap line and commented out the old one but forgot to edit the new one. I set it to 0b00100101 and it works with only 1 byte of data to the RAM.

Is there any way to write to every pixel of the ram at like 12 FPS (202,800 writes per second)? That way I can have a 130 x 130 x 1 array with color info on every pixel and edit the array instead of using the accelerated commands. Or is the RAM writing only useful if you want to draw a background image and then use the accelerated commands to draw lines, etc?

EDIT:

Writing 1 byte for 256 colors v 3 bytes for 256k colors drops write time from about 7 seconds down to 3 seconds to fill the entire RAM with a color. Not bad. Solid colors like Red, Blue, Green look fine under 256, but colors like pink look a little light. It isn't the same pink as I'd expect. I suppose its a sacrifice worth making for better performance.

-robodude666
By Blackfin
#58443
robodude666 wrote:As for the text, it goes slow. Very slow. If I write something 7 characters or under it shows up on the screen in a blink of an eye. However, if I try to print something that is 9 characters long like: "ZOGCASssX" it goes very slow. It shows a single character instantly, but then there is a second delay in between characters. If I send 10 characters, like "ZOGCASshsX" then it renders the first two characters and freaks out, similar to how old tube TVs went fuzzy when losing a signal. It might be a problem with the Arduino, not sure. I'll check the Arduino forums if anyone had similar problems.
That really sounds like the Arduino losing control of its bowels, so to speak. The OLED just sees bytes being written. You're able to write a full screen of a solid colour in 3-seconds (see below) but run into issues processing data accessed via a pointer. Have you looked at the machine code generated by the compiler for this?
Could it be a problem with the string not fitting into the char pointer/array properly? Should I try like having a 12-character global char array that I fill and read from rather than passing the string? I think there is something funky going on Arduino related as using the "scale" variable causes the display to go blank.
As I'm not familiar with the Arduino, I can't do much more than guess. Can you declare something like:
Code: Select all
const char cszString = "Testing Testing";
with the "const" specifying the value to be stored in flash and see how that goes? Or is that what you're doing? If so, then maybe try something like:
Code: Select all
void Test void
{
    int
        i;
    char
        szString[10];
        
    for( i=0; i<9; i++ )
        szString[i] = 'A'+i;
        
    szString[9] = 0;
    
    displayText( szString, <scale>, 0, 0);         
    
}//Test
using whatever you use for scale? That is, store the string on the stack (in RAM) and see if the code performs any differently.
Is there any way to write to every pixel of the ram at like 12 FPS (202,800 writes per second)? That way I can have a 130 x 130 x 1 array with color info on every pixel and edit the array instead of using the accelerated commands. Or is the RAM writing only useful if you want to draw a background image and then use the accelerated commands to draw lines, etc?
In my application, a boost gauge for my car, I use RAM access and GAC. I'll make use of the set column address (0x15) and set row address (0x75) commands to set a region I want to work in and will then draw only what actually needs drawing. For instance, to draw the gauge face, I'll set the region to (0,0)-(127,127) and write in my RLE bitmap of a gauge face. Once written, it doesn't change. I can then set a much smaller region, 15x15 pixels, to draw a digit, draw the digit, then set another region to draw the next digit and so on to get "9.6" or whatever. I might use a small GAC circle of radius 3 as a "pointer" on the gauge face or to simulate LED indicators.

If you've got the processing horsepower to redraw the screen there's nothing stopping you from doing that. If you wanted to write 16384 pixels (128x128) and stuck with the timing for 8800-series processors, you'd have a minimum cycle time of PWlow+PWhigh (you needn't set CS high after each write...you can leave it low and write the bytes in one batch) then the time for one write is ~120nS (60nS + 60nS). 120nS*16384 = 1.97mS. A 12Hz update rate is 83.33mS so you could easily do a 12Hz -- or faster -- update rate if you wanted. Generally though, I find it easier and more memory-efficient to only update the areas of the screen that need it.
Writing 1 byte for 256 colors v 3 bytes for 256k colors drops write time from about 7 seconds down to 3 seconds to fill the entire RAM with a color. Not bad. Solid colors like Red, Blue, Green look fine under 256, but colors like pink look a little light. It isn't the same pink as I'd expect. I suppose its a sacrifice worth making for better performance.
Something is wrong I think. It should be much, much faster than that. I can update the screen with a 128x128 256-colour bitmap in the blink of an eye. I don't actually see the screen being drawn, it just appears. I'm sure I could do some full screen animation if I had the memory. When I draw my boost numbers, the 10ths digit is basically a blur (I have no analog input right now so it's just floating and this noisy...)

How are you doing your writes? Any delays?
By robodude666
#58450
Hey,

Yea.. It is rather weird. Using a char *pointer method I can write up to 14 characters before the OLED freaks out, while a predefined char array[] allows me to draw 11 characters before it freaks out. Funny thing, is char array[] = "TEXT" only allows for up to 7 characters. If I manually set array[0] = 'A' then I can do 11 characters. It is very weird. Never seen anything like this.. I know I have enough memory because I'm using only 6kB out of 14KB that my ATmega168 has.

AH! So that is what the set col and set row address commands are for? You can set the location of where the ram command will start writing from? I thought they were only for initing the OLED. I'll have to read up on them and give them a try. So if I understand correctly... I can, for example, start at 0, 0 and fill the screen with BMP background image. Then use the commands to move around and draw little icon images with lighting fast speed? Excellent! I'll look into it some more.

No, I took out all of my delays except the font functions which are still being used for debugging purposes. I also have a 5mS delay after initing the OLED before I begin doing any work.

I use:
Code: Select all
void send_8bit_serial_data(unsigned int data)
{
  digitalWrite(CS, LOW); //CS(0);
  digitalWrite(RW, LOW); //RW(0);
 
   for(int i = 0; i < 8; i++){
//    digitalWrite(D[i], (data & bitMask8[7-i])); 
    digitalWrite(D[i], (data & (1 << i))); 
  }

  digitalWrite(RW, HIGH); //RW(1);
  digitalWrite(CS, HIGH); //CS(1);
}
to send a byte of data to the OLED. D is an array with the pin # of my D0-7 pins. I originally used a datamask array to get each bit from the data byte, but I replaced it with a i shifted by i times hoping it will save me precious nanoseconds.

Other than that, I use this:
Code: Select all
digitalWrite(DC, LOW);
send_8bit_serial_data(0x5c); //write ram

for(int i = 0; i < 16900; i++){
	digitalWrite(DC, LOW);
	send_8bit_serial_data(0x5c); //write ram

	digitalWrite(DC, HIGH);
	send_8bit_serial_data(0b11100000);

	digitalWrite(DC, LOW);
	send_8bit_serial_data(0xe3); //nop
}
to fill the OLED with a single color (should be blue).

My config is identical to yours basically... I also set the screen on, and clear it before I start to write anything.

Could writing it in ASM give you that much of a boost in performance? Previously, when stuff worked, I could write 40 (7 x 5px) characters in under 700mS to the screen.

Oh, and in case you aren't familiar with Arduino's library digitalWrite sets a pin to HIGH or LOW. For example, something like PORTD.bit0 = 1; would be digitalWrite(0, HIGH);

-robodude666