VGA output

(My monitor is broken, so this project is on hiatus :0 )

Which version of the code are you using? Specifically what is in the interrupt handler code? The interrupt code is (necessarily) quite long, which can cause trouble with other peripherals using interrupts such as serial. However, the later versions of the project will work properly with Serial at 9600, I know because I used it when debugging the code.

I used the REG_PWM version, that works with serial!

void PWM_Handler()
{
  long dummy=REG_PWM_ISR1; 
.
.
}
void setupVGA()
{
  pinMode(42,OUTPUT);   pinMode(43,OUTPUT);                     // vsync=42 hsync=43
  pinMode(34,OUTPUT);   pinMode(35,OUTPUT);                     // blue  (35=msb,34=lsb)
  pinMode(36,OUTPUT);   pinMode(37,OUTPUT);   pinMode(38,OUTPUT); // green (38=msb,37,36=lsb)
  pinMode(39,OUTPUT);   pinMode(40,OUTPUT);   pinMode(41,OUTPUT); // red   (41=msb,40,39=lsb)
  REG_PIOC_OWER= 0x3fc;
  REG_PMC_PCER0= 1<<27; 

  REG_PIOA_PDR |=1<<20;
  REG_PIOA_ABSR|=1<<20;
  REG_PMC_PCER1= 1<<4;
  REG_PWM_WPCR= 0x50574dfc;
  REG_PWM_CLK= 0x00010001;
  REG_PWM_DIS= 1<<2;
  REG_PWM_CMR2=0x0;   //  REG_PWM_CMR2=0x200;  //to invert hsync polarity
  REG_PWM_CPRD2=2668;
  REG_PWM_CDTY2=2348;
  REG_PWM_SCM=0;
  REG_PWM_IER1=1<<2;
  REG_PWM_ENA= 1<<2;  
  NVIC_EnableIRQ(PWM_IRQn);
}

I have a new monitor, so this project is restarted ]:smiley: :smiley: :grin:

I've been working on turning the code into a library, which makes it a lot easier to use. It's about as simple to use as the old TVout library was on the Uno. And I've added some simple text and graphics functions, as well as print library support (so you can use print and println for easy text output). The mini-shield design I posted about above works quite well, but the circuit is simple enough to be built on a breadboard too, just 13 resistors.

I am hoping to make a proper release of the library code in the next week or so. But until then here's a sneak preview of what you can expect.

Code example:

#include <VGA.h>

void setup() {
  VGA.begin(320,240,VGA_COLOUR);
}

void loop() {  
  VGA.setInk(random(256));
  VGA.setPaper(random(256)); 
  VGA.print(" Hello Arduino ");
}

Great info many thanks :slight_smile:

Kamil

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?

Basically yes, the shimmering problem was caused by the interrupt not firing with exactly the same period each time, so each line is a random number of ticks late. What the mysterious piece of inline assembler at the start of the interrupt is trying to do is to synchronize the CPU with the timer. But it is not a good solution.

In the VGA library I tried a different solution. It uses two interrupts each line - the first puts the processor to sleep, so then when the second one fires it should be precisely on time. The display is much more steady (in most cases, rock steady). This broke the Serial support, but in the last few days I think I have made a breakthrough and got it working again. The other advantage to the VGA library is that it now uses DMA for the colour mode which is about 4x faster since the processor can continue to execute code while the screen is being displayed, plus the pixels are all the same width :slight_smile:

Unfortunately I don't have standalone code of this approach as I had already turned the code into a library by then.

@bensom
I see you have some BASIC source code displayed there, does that mean you have a BASIC interpreter running?

Does anybody have plans to port a BASIC interpreter?


Rob

@Graynomad Yes, it runs BASIC, a very limited version from the obfuscated C contests! Arduino Due VGA/BASIC - YouTube

I was coding away on a bit more advanced version, parallel with a simple emulator running on windows but I got on to other things, so that is stalled at the moment. Don't know where to host that code, can send by mail or something if you like!

All inspired by the VGA lib thanks to @stimmer, great work! Just tried the PAL version on a little car DVD panel with medium succes. It shows very nice stable black and white, but lots of color fringe. I suspect my soldering, will try that again soon.
Also, doesn't composite video need a 75 ohm resistor to ground?

edit: Got the color working with an R 2R ladder made of 68 and 150 ohm resistors, looks much better now.

Composite video only needs the 75 ohm load resistor if the input to the TV is high impedance. Almost all TVs have a 75 ohm input impedance already so the load resistor isn't needed - the TV is the load :slight_smile:

According to the data sheet there is a slight output resistance on the digital outs, you may find 82 ohm resistors work better than 68. Try it, it will either look better or worse. I did write a sketch to test how monotonic the DAC is - put a 75 ohm load resistor in place for this (doesn't have to be exactly 75 ohms) and connect the output to analog in 0:

// array runs lsb to msb, you can add/remove 
// values according to the number of resistors
// in your DAC
char pins[]={34,35,36,37,38,39,40,41};

void writeval(int v){
  for(int i=0;i<sizeof(pins);i++){
    digitalWrite(pins[i],v&1);
    v>>=1;
  }
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);
  for(int i=0;i<sizeof(pins);i++)
    pinMode(pins[i],OUTPUT);
}

void loop() {
  int l=0;
  for(int i=0;i<(1<<(sizeof(pins)));i++){
    writeval(i);
    delay(10);
    int m=analogRead(0);
    printf("%d : %d  d=%d\n",i,m,m-l);
    if(m<l)printf("!!!!!!ERROR!!!!!!!!!! non-monotonic\n");
    l=m;
  }
  delay(2000);
}

Because of the number of technical compromises that have to be made to get a colour composite signal at all, there will always be a lot of colour fringe. The output signal is way off spec in a number of areas. I aimed low with that project, the goal was VHS quality rather than broadcast quality :wink:

a very limited version from the obfuscated C contests!

I hope you didn't have to work with the obfuscated source code.

I'm not personally interested in BASIC but it is very popular with many people and might be a way to convert Picaxe and Maximite users to Arduino.

The Maximite has a pretty good interpreter that was/is open source, maybe it's worth porting that across. For that matter there has to be 100s of interpreters around.


Rob

Rob, actually, I did de-obfuscate that interpreter, only to find the deobfuscated version when I was done, oh well. A sunday well spent. BASIC on the Due sure made my Apple][ days come back to me!
And yes, there are lots of BASIC interpreters to be found, sure one should fit the arduino due pretty well. I might continue on what I have, if it rains a lot this summer.

Stimmer, I did something similar, but looked at it with the scope, seems decently linear.

void setup() {
    pinMode(36,OUTPUT);    pinMode(37,OUTPUT);
    pinMode(38,OUTPUT);    pinMode(39,OUTPUT);
    pinMode(40,OUTPUT);    pinMode(41,OUTPUT);
    REG_PIOC_OWER = 0xFFFF;
}
int i=0;
void loop() { // C.4 - C.9
    REG_PIOC_ODSR = i<<4;
    if (++i>64) i=0;
}

I really like the VHS quality on this, 40 columns of text and all that! That space-age, Z80/6502 look :slight_smile:
Will try with 82 ohm resistors, but unloaded the voltages seems pretty spot on, according to the composite video wiki page.

Hi everyone,

I have an idea but I not sure if it is possible. Let me to tell you... I imagine that on Arduino Due.

If I am using digital pin to control access to a RAM (address, data, ES and W/R pins ).
I could easier store data in this RAM.

With the VGA lib (modified) I'll enough time to get data from the RAM to built my bitmap or it's just a dream ?

Eddy

Your first problem is that the Due doesn't really support the external memory function because they didn't break out all the pins. You can kludge something up but it's just that, a kludge, and I can't remember what (if any) limitations there are with that approach.

One of the guys has a thread about it on the forum here.

As for the timing I'm not sure, but as you can see it's working with internal RAM, external will be slower but I would think still fast enough.

One question is what will you do with all that RAM, it's not required for a text display or even a lo-res graphic display.


Rob

tks about your answer Graynomad,

Of course external RAM will be slower but may be enough to be just a bitmap.
If it's the case we could display a resolution better with more colors.
If you have a RAM you make a kind of double buffering.

About pins I read that Arduino Due have 54 digital I/O pin.
I don't know very well this product but I'm imaging :
10 pins for vga
8 pins for data RAM
and others for addressing RAM and control.

8 pins for data RAM
and others for addressing RAM and control.

That's sort of correct BUT you have to have the right pins if you want real XM access, and the Arduino team did not see fit to provide them so you will have to dick with the board and solder wires in place.

You can of course bit bang the interface using any old pins but that will be incredibly slow in this context.

As I said though external RAM has been implemented by someone but I can't remember by who or where the post is.

IMO the Due as it stands is not an appropriate platform for hi-res VGA, but if you can properly implement XM it might be OK.


Rob

ok tks

I found that

http://arduino.cc/forum/index.php/topic,20025.0.html

That thread is about the Mega, an entirely different processor and as such of little use to you with a Due.


Rob

Stimmer,

Hats off for a job well done. I have a really cool sunlight readable Sharp 6.5" NTSC on my site at EarthMake for $69. It has an RGB input and NTSC timing. This means it should take a lot less bandwidth out of the due because ntsc is only 1.4318 MHz versus 25 MHz for vga. Check it out here:
http://earthmake.com/product/amt-002/
If it's something you want to play with email me at randy at earthlcd com and I'll get you one.

Randy

Look at the VGA library thread (in my sig). NTSC is supported, but the bandwidth for NTSC is actually nearer 6MHz, so needs a sample frequency over 12MHz to generate (I use 14MHz). And as the VGA colour mode is half-resolution I use 14MHz for that too. So the bandwidth is the same, but as NTSC is a more complex signal it actually takes much more out of the Due, not less. (VGA can be done with DMA alone, but NTSC requires processing as well to do the mathematics).