SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By Blackfin
#58461
robodude666 wrote: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.
Certainly is quite strange, isn't it? I really think the OLED "freaking out" is actually the processor itself going wonky and sending nonsense to the OLED.
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.
Yes, exactly. And what's really cool is that when you set it up, you set for a row (or col) increment after each write and so when you do a full-screen write, you just write 16K worth of data, as you've done. When you create a region as I call it, the same principle applies. So for a 15x15 bitmap, it'll do 15 pixels across and then reset to the start column of the region, bumping the row by one. With icons like this, and the use of "dirty" flags indicating what needs updating, you can save alot of time by only drawing what actually needs updating.

I don't see anything really out of the ordinary in your code that sticks out as a "Aha! There's your problem!" I will say that you don't need to send the "write RAM" command after each byte nor should you need to send a NOP.

As a general speed test, maybe you can try something like this:
Code: Select all
   //make sure start col and row commands issued
   //with 0,0 - 127,127 as parms before getting here
   digitalWrite(DC, LOW);
   send_8bit_serial_data(0x5c); //write ram
   //put data for blue pixel at port just once
   for(int i = 0; i < 8; i++){
      digitalWrite(D[i], (0xe0 & (1 << i)));
   digitalWrite(DC, HIGH);
   digitalWrite(CS, LOW); //CS(0); 
   digitalWrite(RW, LOW); //RW(0); 
   //now pulse the RW line to fill screen
   for(int i = 0; i < 16900; i++){
       digitalWrite(RW, HIGH);
       //may need to wait 60nS min here if processor speedy
       digitalWrite(RW, LOW);
       //may need to wait 60nS min here if processor speedy
} 
   digitalWrite(CS, HIGH); //CS(1); 

Does this run any differently?
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.
I shouldn't think so. I'm sure if you were to look at the resulting machine code generated by your C compiler you'd see it had done a decent job and created pretty clean code. Then again, if it's taking nearly a second to write out just 1400 pixels (5x7x40) when it's theoretically possible to write 16000 pixels in less than 2mS, something's wrong. How fast is the processor itself running? Can you set up, say, an LED to blink (i.e. port pin toggle) at 4Hz and actually get that?
By robodude666
#58499
I played with the set row/col stuff last night after making my post. Pretty cool stuff. When I first asked about writing to RAM, you said there's no way to tell it where to write to ;). To draw a line or a rectangle, would it be more time efficient to change the col/row for every pixel or just use the built in accelerated command?

I tried that blob of code and it fills the screen with color in almost a blink of an eye, The first 2/3rd are instant and then I can see the bottom 1/3rd get filled shortly after. But, what if I every pixel has a different color? Then I would need to change colors between the RWs and that would cost time.

Well, that 700mS was when I was in 256k color mode. 16900 px / 1400 px * 700mS per pixel = 8.5 seconds. When using 256k color mode to fill a screen it took about 7-8 seconds so it does make sense. What I don't understand is why I am getting such poor performance.

The Arduino doesn't make an asm file. Inside of the build folder I see a bunch of "o" files for the compiled Arduino headers, a cpp file for my script, and a elf, hex, and rom file.

The Arduino's ATmega168 runs at 16MHz I believe. I never tried it at 4Hz, but I know it can be done much faster than that. With delays it is possible to make it blink at 4Hz. I know you can manually setup a timer interrupt and get it close to 4Hz.

-robodude666
By Blackfin
#58502
robodude666 wrote:I played with the set row/col stuff last night after making my post. Pretty cool stuff. When I first asked about writing to RAM, you said there's no way to tell it where to write to ;).
I did? I certainly didn't mean to imply that.
To draw a line or a rectangle, would it be more time efficient to change the col/row for every pixel or just use the built in accelerated command?
For circles, I use the GAC. I couldn't get the line draw GAC command to work for all lines and gave up and wrote my own line algorithm though I used the line-draw GAC command to give me a "setpixel" function (start and end points the same.) It's clunky but it worked.
I tried that blob of code and it fills the screen with color in almost a blink of an eye, The first 2/3rd are instant and then I can see the bottom 1/3rd get filled shortly after. But, what if I every pixel has a different color? Then I would need to change colors between the RWs and that would cost time.
First, I don't know why it would fill 2/3rds at one rate and then slow for the last 1/3rd. Second, you're correct, to write different bytes you'd need to update the data byte each time (or, for RLE, whenever your run count hits zero.) So try that:
Code: Select all
   //make sure start col and row commands issued
   //with 0,0 - 127,127 as parms before getting here
   digitalWrite(DC, LOW);
   send_8bit_serial_data(0x5c); //write ram
   //put data for blue pixel at port just once
   for(int i = 0; i < 8; i++){
      digitalWrite(D[i], (0xe0 & (1 << i)));
   digitalWrite(DC, HIGH);
   digitalWrite(CS, LOW); //CS(0);
   digitalWrite(RW, LOW); //RW(0);
   //now pulse the RW line to fill screen
   for(int i = 0; i < 16900; i++){
       digitalWrite(RW, HIGH);
       //may need to wait 60nS min here if processor speedy
       digitalWrite(RW, LOW);
       //may need to wait 60nS min here if processor speedy

      //add this to simulate changing bytes every loop
      for(int j = 0; j < 8; j++){
         digitalWrite(D[j], (0xe0 & (1 << j)));

}
   digitalWrite(CS, HIGH); //CS(1); 
Just add the last two lines to the loop to simulate this. How does the drawing rate change? If it goes much slower, it would appear the method by which you have to update your parallel bytes -- shifting like this -- is very slow.

With the 9S08, I just have an 8-bit port. I set a pointer to the image I want to draw and do something like:
Code: Select all
do{
port = *Ptr;
.
.
.
Ptr++
}while( ... )
Or, in assembly:
Code: Select all
ldhx #Source
L1:
lda   ,x
beq   L2

sta   Port
;toggle WR line
aix   #1
bra  L1

L2:
...
The fact that you are forced to bit bang a parallel port is really unfortunate. Is this the only way to do it with your processor?
By robodude666
#58504
Well, it isn't like the first 2/3rds are extremely fast and the last 1/3rd is slow. They are both fast. I guess it is just the refresh rate is low enough to see it being filled.

That second test was significantly slower. The Arduino does have digital pins 0-7 on PORTD's bits 0-7. I can do PORTD = data; but I use an array to remove hardware dependencies. In case the newer Arduinos use different chips or different pinouts I won't be affected that badly.

I run this code:
Code: Select all
   for(int i = 0; i < 16900; i++){
       digitalWrite(RW, HIGH);
       digitalWrite(RW, LOW);
       
       ran = random(0,255);
       for(int j = 0; j < 8; j++){
         digitalWrite(D[j], (ran & (1 << j))); 
       }
  }
and it filled the screen in about 7 seconds, while:
Code: Select all
   for(int i = 0; i < 16900; i++){
       digitalWrite(RW, HIGH);
       digitalWrite(RW, LOW);
       
       PORTD = random(0,255);
  }
took only 5 seconds to execute. Using the bit banging is 2414 pixels/second while direct write to PORTD is 3380, a 40% increase in performance. If, instead of using random(0,255) to get a color but rather I use i % 255 then the bit-banging method takes 2 seconds while writing directly to PORTD takes the same amount of time as the original method without changing the color. Pretty impressive changes!

I suppose for the sake of performance I will write to the PORT instead of bitbanging each bit. However it still takes 5 seconds to fill the screen which is a tad disappointing. I suppose I will have to use a solid color for the background and have it filled in <500mS and then draw icons instead of having a gradient background.

I will rewrite the rest of my program to use the PORTD = data; method, as well as reorder my digitalWrites for the pins.

As for custom line algorithms, it is a good idea but Justin's 3D OLED program has a simple function which reorders the x1, y1 and x2, y2 values so that it is compliant with A < B < 132; C < D < 132; You should check it out as it may save some processing time.
By Blackfin
#58505
robodude666 wrote:As for custom line algorithms, it is a good idea but Justin's 3D OLED program has a simple function which reorders the x1, y1 and x2, y2 values so that it is compliant with A < B < 132; C < D < 132; You should check it out as it may save some processing time.
I don't think his algorithm fixed it. I might be wrong but he says that all he needed were horizontal lines.

A line from (40,100) to (64,64) cannot be reordered in any way to meet the A < B < 132; C < D < 132 restriction. If (A,C) == (40,100) and (B,D)==(64,64) then:

A<B<132 == 40<64<132 okay
C<D<132 == 100<64<132 fails

If I reorder the coordinates to draw the other way, so that (A,C) == (64,64) and (B,D)==(40,100) then:

A<B<132 == 64<40<132 fails
C<D<132 == 64<100<132 okay

This line cannot be fixed, its end points cannot be moved in any way to allow the GAC to draw it, such that you get the desired line.

I think I had some success back when I was trying to get it to work if both conditions "failed". That is, a line from (64,64) to (40,40) actually worked if I recall. But a native line drawing routine that can't draw certain lines is no use. It's a bummer because even a modified Bresenham function which I wrote is still relatively slow though I had pretty good success with it.
By robodude666
#59459
Hey,

Been a busy because of class work lately, but had some time to play around with the OLED lately. I've been trying to display a small 16 x 4px image on screen by saving it in an array (I wrote a small PHP script to create a 1 byte color code for each pixel), however nothing draws. When I try to serial out the info that is being outputted from the array I get jumbled random numbers. Any idea what could be wrong? I have been trying to figure out whats wrong for a number of days. I have plenty of memory left so I have no idea. I am able to serial out the index values perfectly fine though.

I can do stuff like:
Code: Select all

for(int i = 0; i < 16900; i++){
//.. stuff
PORTD = i % 63;
//.. stuff
}
just fine, but when I try:

PORTD = image[i % 63];

the OLED goes crazy from random junk.

Any thoughts?

-robodude666
By Blackfin
#59511
From the look of it, you're trying to "paint" or wallpaper the OLED with copies of this image. Is that correct?

If so, remember that you need to see the start and stop row and columns for each copy of the bit map. Each of these regions will take 64 bytes, corresponding to the image data. After the 64th write, re-set the region and repeat. For example, a quick C routine:
Code: Select all
void WallPaperScreen( void )
{
    unsigned char
        row, col;
    unsigned char
        rbase, cbase,
        pixels;
        
    //OLED is 128x128
    //bitmap is 16x4 (assume 16 tall by 4 wide)
    //we can fit 32 images across (128/4)
    //we can fit 8 images vertically (128/16)    
    for( row=0; row<8; row++ )
    {
        rbase = row << 4;
        
        for( col=0; col<32; col++ )
        {               
            cbase = col << 2;
            
            //set operating region
            OLED_WriteCommand( SET_COLUMN_ADDRESS );
            OLED_WriteData( cbase );
            OLED_WriteData( cbase+3 );
    
            OLED_WriteCommand( SET_ROW_ADDRESS );
            OLED_WriteData( rbase );
            OLED_WriteData( rbase+15 );
            
            //make sure writes go to display RAM
            OLED_WriteCommand( WRITE_RAM_ENABLE );
            
            //now write 16x4 image to region
            for( pixels=0; pixels<64; pixels++ )
                OLED_WriteData( image[pixels] );
                
        }//for
        
    }//for                        

}//WallPaperScreen

If you just want to draw a single instance of it, set the row and col start/stop values once and draw it once.
By robodude666
#59630
Hey,

Well.. I don't want to wallpaper the display. I just want to draw an image a single time. I set the for loop to be huge so I can view the image, as for some reason the screen blanks out when it is done with a for loop ram draw.

It turns out writing software for a dual core 2.4GHz system w/ 4GB ram system is different than a small 16MHz chip with 16KB flash + 1KB SRAM. My fonts array was about 800byte and I had a bunch of other variables. The image I defined was 128byte which passed the 1KB limit on the chip. I removed my fonts related stuff and my code works perfectly fine now :) I just now need to make the image a bit larger as 16 x 4 is teeny tiny. Might try drawing an emoticon now! I'm also going to need to find ways to save stuff. I read that I can put PROGMEM after a variable's name, and it will be stored as part of FLASH instead of SRAM. Will give that a try as I have 14KB fee.

Cheers,
-robodude666
By busonerd
#59654
yup - but you also need to use special functions to read from PROGMEM variables. If you do that, you should have no problems.

Cheers,

--David Carne
By robodude666
#60203
Howdy,

Been having some success lately. I reinstalled GD2, and rewrote my PHP script to better analyze the image file. I know it isn't the best language to use, but I've been a web developer for the past 8 years, and it is just quicker than learning a new library in C or Python, etc.

I've ran into a small problem which I have sort of found a solution for, but not completely it seems.
Code: Select all
   digitalWrite(DC, LOW);
   digitalWrite(CS, LOW);
   send_8bit_serial_data(0x5c); //write ram
   digitalWrite(CS, HIGH);

   digitalWrite(DC, HIGH);
   digitalWrite(CS, LOW);
   digitalWrite(RW, LOW);

   for(i = 0; i < (16 * 16); i++){
       digitalWrite(RW, HIGH);
       delayMicroseconds(100);
       digitalWrite(RW, LOW);
       delayMicroseconds(100);

       PORTD = smile[i];

       delayMicroseconds(100);

  }
  digitalWrite(CS, HIGH);
That is the code I use draw an emoticon (eh) to the screen. However, for some reason, no matter what I draw the very right column of pixels shows up on the very left of the image. I noticed that if I comment out the

digitalWrite(RW, LOW);

line right before the for loop then the problem is solved, however, it then introduces a 1px blank line in between each of my lines. This causes the image to be double its normal height, and gives it a "scan line" effect which I don't want.

any ideas?



I also did look into the PROGMEM more, and it seems you do need a special set of pgm functions, which isn't a problem. All they do is fetch the information for you. From the examples I've seen, you need to use the pgm functions to get the data and use a copy function to place the data into a buffer, correct? Doesn't that defeat the purpose, sort of? Or am I able to only buffer a couple bytes at a time, draw them, and fetch the rest?

Cheers,
-robodude666
By Blackfin
#60205
In your loop, you appear to send RW high then low before writing your first byte of image data to the port. Why not put the byte at the port and then hit RW?
By robodude666
#60207
Huh, interesting. Switched it around so that the data gets written before RW goes high and it works perfectly now! Thanks. Reason I had it like that, is because you wrote it like that in all of the examples so far.

EDIT:

I just noticed that reordering things is exactly the same thing that I tried earlier. It causes little blank lines to appear in between my pixels.

Image

Is that how it is supposed to look like? Is it normal for their to be spacing in between pixels like that? I haven't noticed it earlier. Before it seemed like everything was drawn perfectly tight next to each other.
By Blackfin
#60212
robodude666 wrote:Huh, interesting. Switched it around so that the data gets written before RW goes high and it works perfectly now! Thanks. Reason I had it like that, is because you wrote it like that in all of the examples so far.
If you're referring to my post of Nov 6, you'll notice that you were trying to fill the screen with blue. In that example, I ensured that the port had the value for the blue pixel before the toggling in the loop. In the example where you were trying to track down the differing rates of screen drawing, I included the code line where the data value was changed at the end of the loop merely to simulate the changing of a dataport during the fill progress, not to actually write valid image data to the screen. Regardless, it would still work since the first time RW was toggled, valid data would have been present at the data port.

Regardless, it makes sense that the data you want to write to the OLED should be at the port before you wiggle the RW line.
Is that how it is supposed to look like? Is it normal for their to be spacing in between pixels like that? I haven't noticed it earlier. Before it seemed like everything was drawn perfectly tight next to each other.
You're zoomed in pretty close there but it looks normal to me. Try doing it both ways and shooting pics of each for comparison. What's your colour depth?