Using SSD1327 128x128 4-bit display

The sprites imply overwrite the background, but in the second test (64fps) I’m checking for byte==0, which only checks if both odd and even pixels are zero within each 2 pixel byte. I haven’t tested yet at an individual pixel level, I’m tempted to think it would be too slow.

All locals do indeed get ‘killed’ by the end of the function,
but it’s the register demand during the function that you need to worry about.

AVR chips only have 32x8-bit registers, and certain instructions only work with certain registers.
E.g. ‘add immediate to word’ (roughly reg += constant, where reg is 16-bits wide) only works on 4 of the register pairs (8 of the 32 registers), ‘compare with immediate’ only works for the ‘upper’ 16 registers.

As a result of the constraints, it’s easy for the CPU to end up wasting cycles simply juggling registers around to free up a register that can do what it needs to do.

I think roughly it would be 1 extra branch, possibly 1 extra compare (depending on whether or not an ‘and’ sets the zero flag), definitely 2 extra 'and’s.


I’ve been thinking, if there were some way to avoid having to do this 8 step loop for each scanline you could probably save quite a bit of processing power.

Unfortunately I don’t fully understand what it’s doing,
so I can’t suggest anything yet.
(For one thing I’m still not clear on exactly what SPRITES::offset is.)

(Which reminds me, why is the type SPRITES, plural and in macro case, but the actual array is sprite, singular? Surely the type should be singular because it describes a single entity and the array should be plural since an array contains multiple items?)


Come to think of it, I’m not sure that % 8 is needed:

spriteReference.offset = (((lineY - spriteReference.y) % 8) * 4);

The prior condition:

if((lineY >= spriteReference.y) && (lineY <= spriteReference.y + 7))

Would imply that (lineY - spriteReference.y) can only be between 0 and 7 (inclusive).
(Unless spriteReference.y + 7 overflows of course, but that shouldn’t be possible because of (lineY >= spriteReference.y) and lineY being < 128.)

That only saves you an & 0x7, but at least it’s something.


There’s a potential buffer overrun bug with the current code…

for(uint8_t offset = 0; offset < 4; ++offset)
  scanLine[spriteReference.x + offset] = (spriteIndex + 1);

If spriteReference.x is 125 or more then scanLine[spriteReference.x + offset] overruns the buffer.

But for the SamD am i rite?

Indeed, you just got the fps up to 65 :slight_smile:

1 Like

I assume simply increasing the size of the buffer bye a few bytes would work out faster than checking bounds?

Or do the bounds check once, early. (if possible)

Yes, but you’d be paying for the privilege with RAM, which isn’t exactly plentiful on an AVR chip.
(Though it’s only 3-4 bytes, assuming spriteReference.x is never more than 127, so it might be worth it.)

One alternative would be to wrap the index using modulo (which would compile to an & because you’re using a power-of-two for size), which would have the side effect of sprites drawn near the right edge of the screen wrapping around to the left edge (which depending on how you lookt at it is either a bug or a feature).

But theoretically that would cost an extra 1 or 2 cycles per loop,
which is going in the opposite direction of what you want (unless you think the wrapping is a neat feature).


I was about to say that the ‘proper’ solution is:

for(uint8_t offset = 0; offset < 4; ++offset)
{
  const size_t index = (spriteReference.x + offset);

  if(index >= 128)
    break;

  scanLine[index] = (spriteIndex + 1);
}
And suggest something crazy...
const auto x = spriteReference.x;
const auto index = (spriteIndex + 1);

switch(x)
{
	case 125:
	{
		scanLine[x + 2] = index;
		scanLine[x + 1] = index;
		scanLine[x + 0] = index;
		break;
	}
	case 126:
	{
		scanLine[x + 1] = index;
		scanLine[x + 0] = index;
		break;
	}
	case 127:
	{
		scanLine[x + 0] = index;
		break;
	}
	default:
	{
		scanLine[x + 3] = index;
		scanLine[x + 2] = index;
		scanLine[x + 1] = index;
		scanLine[x + 0] = index;
		break;
	}
}

Or even suggest dropping into assembly to write a jump table
(which admittedly might still be faster than what I’m about to propose),
but then I had a flash of inspiration and came up with this:

for(uint8_t index = spriteReference.x; index < 128; ++index)
  scanLine[index] = (spriteIndex + 1);

Which shouldn’t be much more expensive than what you’ve already got (if not the same even).

Slightly off topic but…

Adding sound doesn’t seem to get in the way much :grin:

1 Like

Interesting that sometimes there is screen flicker and some times there is not.

Phones are crap. Keeps trying to compensate for brightness changes.

For better recordings you can try adjusting the refresh clock (command 0xB3 “Set Front Clock Divider /Oscillator Frequency” bits A[7:4]) until it matches the camera better.

Dropping the room’s brightness also helped my camera a lot with the longer exposure.

1 Like

128x128_screen_20_24_1.zip.bin (9.9 KB)

Here is the latest source. It’s slowed down a bit since adding polyphonic music, not sure how to go about speeding that up.

—hmmmm, I had an idea to use this screen in a dreamcast VMU, but I would have to use it upside down. However I can’t see any commands for screen rotation or orientation anywhere. Does anyone know if it’s possible in the driver at all or would I have to redo the update code?

[edit] - Manually flipped the screen, I can’t help thinking there might be abetter way than this -

  if(flipScreen){\
    pixel = pgm_read_byte(&alltiles[lineTile][31-lineOffset]);\
    pixel = ((pixel&0x0F)<<4) | ((pixel&0xF0)>>4);\
  }else{\
    pixel = pgm_read_byte(&alltiles[lineTile][lineOffset]);\
  }\

and

  if(flipScreen){\
    tile = myMap[255-tileIndex];\
  }else{\
    tile = myMap[tileIndex];\
  }\

Anyone else playing with this screen? mine seems to only init correctly after physically disconnecting power. Soft reset, when uploading sketch for example leaves the screen garbled :frowning:

Yeah played with it for my homemade package. no issues with initializing though. Does the screen get garbled too after pressing the reset button?

It does, I’ll post some pics when I get a chance. Sometimes the image is blurry, sometimes it seems to have set the column/row start address wrong, others it’s just a mess.

[edit] using the following project _Breakout_200203.zip.bin (9.9 KB)

I’m currently getting, after full power down, perfect screen. After re-upload of sketch I get random offset, after second upload of sketch I get blank screen.
I wonder if my init code is wrong somehow?

[edit] Code is fine, looks like the reset pin is broken.

1 Like

Anyone got any ideas for a case a little bigger than a vmu? I’ve killed too many screens trying to cram them in there :disappointed:.

Was the pocketstation any bigger? Or a Pokemon mini?

Both very expensive, especially for this sort of thing :thinking::stuck_out_tongue_winking_eye:

[edit] I think it’s time I gave up on this idea. My source code is posted multiple times through the thread, I hope someone else feels like continuing.

1 Like

Does anyone see a simple way to power this screen (+pro micro) from 2xAAA rechargeable batteries? Is it more than just a voltage issue at play?

You could use one of those cheap DC-DC converter modules

1 Like