I reworked my code based off the REG_PWM version and am no longer having issues with the Serial connection.
My code is able to load a bitmap graphic off the SD card, read the bitmap and DIB headers and resize any image to the desired 2D-array size. It has to down-sample from 24-bit color to 8-bit color, but still looks pretty good with only 256 color choices.
Currently, I'm loading a background image to fit the 320x240x8 framebuffer.
Then I load 2 smaller images to do some bit-blit. I'm using images that are 24x32 pixels (also down-sized and down-sampled from larger images).
void readBitmap(char* filePath, byte** pictureBuffer,int bufferWidth,int bufferHeight)
{
SdFile myFile;
boolean openedFile = myFile.open(filePath,O_RDWR);
if (openedFile)
{
unsigned int FileSize=0;
unsigned int Offset=0;
Serial.print("Reading ");
Serial.print(filePath);
Serial.println(" bitmap header...");
//Read Bitmap header
for (int i=0;i<14;i++)
{
byte myByte=myFile.read();
Serial.print("Byte ");
Serial.print(i);
Serial.print(": 0x");
if (myByte<16)
{
//Pad with a leading zero, if needed.
Serial.print("0");
}
Serial.println(myByte,HEX);
//Bytes 2 through 5 are the file size
if((i>=2) && (i<=5))
{
FileSize=FileSize+(((int)myByte)<<(8*(i-2)));
Serial.print("FileSize: ");
Serial.println(FileSize);
}
//Bytes 10 through 13 are the offset to the pixel data.
if((i>=10) && (i<=14))
{
Offset=Offset+(((int)myByte)<<(8*(i-10)));
Serial.print("Offset: ");
Serial.println(Offset);
}
}
Serial.print("Done reading ");
Serial.print(filePath);
Serial.println(" bitmap header.");
Serial.print("Reading ");
Serial.print(filePath);
Serial.println(" DIB header...");
unsigned int Width=0;
unsigned int Height=0;
unsigned int BitsPerPixel=0;
//Read the DIB header
for (int i=0;i<40;i++)
{
byte myByte=myFile.read();
Serial.print("Byte ");
Serial.print(i);
Serial.print(": 0x");
if (myByte<16)
{
//Pad with a leading zero, if needed.
Serial.print("0");
}
Serial.println(myByte,HEX);
//Bytes 4 through 7 are bitmap width in pixels
if((i>=4) && (i<=7))
{
Width=Width+(((int)myByte)<<(8*(i-4)));
Serial.print("Width: ");
Serial.println(Width);
}
//Bytes 8 through 11 are bitmap height in pixels
if((i>=8) && (i<=11))
{
Height=Height+(((int)myByte)<<(8*(i-8)));
Serial.print("Height: ");
Serial.println(Height);
}
//Bytes 14 through 15 are bitmap bits per pixel
if((i>=14) && (i<=15))
{
BitsPerPixel=BitsPerPixel+(((int)myByte)<<(8*(i-14)));
Serial.print("BitsPerPixel: ");
Serial.println(BitsPerPixel);
}
}
int BytesPerPixel=((FileSize-Offset)/Width)/Height;
Serial.print("BytesPerPixel=");
Serial.println(BytesPerPixel);
Serial.print("Done reading ");
Serial.print(filePath);
Serial.println(" DIB header...");
Serial.print("Loading ");
Serial.print(filePath);
Serial.println(" bitmap pixels...");
myFile.seekSet(Offset+1);
byte buf[Width*BytesPerPixel];
for(int y=0;y<Height;y++)
{
//Serial.print("Loading Row: ");
//Serial.print(y,DEC);
//Serial.print(" / ");
//Serial.println(Height,DEC);
//Serial.print("Pointer to FB[y][0] = ");
//Serial.println((int)&FrameBuffer[y][0],HEX);
//Serial.print("Loading Pixel: ");
//Serial.print(x,DEC);
//Serial.print(" / ");
//Serial.println(Width,DEC);
//Read the entire row of pixels all at once.
//This is a major performance upgrade.
myFile.read(buf,sizeof(buf));
//Convert the 3-bytes-per-pixel data into 1-byte-per-pixel format.
for (int x=0;x<Width;x++){
byte red=buf[x*3];
byte green=buf[(x*3)+1];
byte blue=buf[(x*3)+2];
//Serial.print("Red = ");
//Serial.println(red);
byte redBits = (byte)map(red,0,255,0,7);
//Serial.print("RedBits = ");
//Serial.println(redBits);
byte greenBits = (byte)map(green,0,255,0,7);
byte blueBits = (byte)map(blue,0,255,0,3);
pictureBuffer[(bufferHeight-1)-(int)(((double)y*(double)bufferHeight)/(double)Height)][(int)(((double)x*(double)bufferWidth)/(double)Width)]=(redBits<<5)+(greenBits<<2)+(blueBits);
}
//The Bitmap file format pads every row to a 4-byte (word)
//boundary. Calculate the padding, and skip it.
int BytesInLastWord=((Width*BytesPerPixel)%4);
int BytesOfPadding=0;
if (BytesInLastWord>0)
{
BytesOfPadding=4-BytesInLastWord;
}
//Serial.print("Row should have ");
//Serial.print(BytesOfPadding);
//Serial.println(" bytes of padding.");
for (int i=0;i<BytesOfPadding;i++)
{
//Skip the padding byte.
myFile.read();
}
}
myFile.close();
/*
for (int y=0;y<Height;y++)
{
for (int x=0;x<Width;x++)
{
if (FrameBuffer[y][x]<16)
{
//Pad the output with a leading zero.
Serial.print("0");
}
Serial.print(FrameBuffer[y][x],HEX);
Serial.print(",");
}
Serial.println();
}
*/
}
else
{
Serial.print("Error opening file ");
Serial.print(filePath);
Serial.println(" for read.");
}
Serial.print("Done loading ");
Serial.print(filePath);
Serial.println(" bitmap pixels.");
}//End readBitmap method
I bit-blit the images using the following simple steps:
void loop(void) {
if (millis()-starttime>100)
{
//Initialize our timer
starttime=millis();
if (spriteDeployed==true)
{
//Erase the sprite by putting the background back.
for (int y=0;y<32;y++)
{
for (int x=0;x<24;x++)
{
FrameBuffer[locationY+y][locationX+x]=background[y][x];
}
}
spriteDeployed=false;
}
locationX=locationX+random(-5,6);
if (locationX>320-24)
{
locationX=320-24;
}
if (locationX<0)
{
locationX=0;
}
locationY=locationY+random(-5,6);
if (locationY>240-32)
{
locationY=240-32;
}
if (locationY<0)
{
locationY=0;
}
//Get the background for the new location
for (int y=0;y<32;y++)
{
for (int x=0;x<24;x++)
{
background[y][x]=FrameBuffer[locationY+y][locationX+x];
}
}
//Put Bit-Blit the sprite on the screen
for (int y=0;y<32;y++)
{
for (int x=0;x<24;x++)
{
//The "And" (&=) operation allows for white background to be
//transparent, and the black object to overwrite the background.
FrameBuffer[locationY+y][locationX+x]&=Sprite[y][x];
//The "Or" (|=) operation allows the whte background to remain
//transparent, while overlaying the color object on the black area.
FrameBuffer[locationY+y][locationX+x]|=SpriteMask[y][x];
}
}
spriteDeployed=true;
}
}//End loop
This all works, except there's a slight flicker to the display. It's not bad, but bugs your eyes a little. The pixels seem to shift slightly left and right as the screen refreshes. It seems that the h-sync or v-sync timings must be off just slightly every once in a while. Is that possible with the interrupts? I'm trying to decide what to do to fix the image stability. Any thoughts?