VGA output

canter:
This project can work with arduino uno?
Thanks.

No, the UNO only has enough memory for something like a 120x96 black and white image.

If that is fine, there are two shields that use the TVout library that are supposed to make the job easier:

I have the Video Experimenter, but it is on my to-do pile, and I haven't tried it out yet

Superb!

This is very usefull for me - thanks

Is possible post source code for 640x480 or 800x600 black/white ?

Many thanks

Kamil

How long before we see a GPU or a graphics card from 1995 get modified to work with due/uno and a VGA monitor? :slight_smile:

why 95?
Someone have done it with stm32 based boards: STM32 Driving A PCIe Video Card | Hackaday
and Gameduino produces vga(I think) using an FPGA adapterboard.

and, btw, the Propeller, with its 8 "core's" produces vga for games and such, if one could interface more memory on the Due (wish that could be possible on the next SAM based card) one could have kinda ok graphics.

Simple Game of Life example :slight_smile:

#define GRID_X 100
#define GRID_Y 92

#define MAX_GENERATIONS 250

uint8_t grid[2][GRID_X][GRID_Y];
uint8_t current_grid = 0;
uint8_t generations = 0;

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
byte fb[240][320];

#define do20(x) x x x x x x x x x x x x x x x x x x x x
#define do80(x)  do20(x) do20(x) do20(x) do20(x) 
#define do320(x) do80(x) do80(x) do80(x) do80(x) 
#define MNOP(x) asm volatile (" .rept " #x "\n\t nop \n\t .endr \n\t")

void TC0_Handler()
{
    long dummy=REG_TC0_SR0; 
                           
    if(line < 480){        
        byte * p=fb[line>>1];
        MNOP(160);
        do320(REG_PIOD_ODSR = *p++;MNOP(2);)
        REG_PIOD_ODSR = 0;
    }      
    if(line==490) digitalWriteDirect(3,1); //or digitalWriteDirect(3,0); to invert vsync
    if(line==492) digitalWriteDirect(3,0); //or digitalWriteDirect(3,1); to invert vsync
    
    line++; if(line == 525) line=0;
}

void setup(){
  
  pinMode(3,OUTPUT);  pinMode(2,OUTPUT);                      // vsync=3 hsync=2
  pinMode(25,OUTPUT); pinMode(26,OUTPUT);                     // blue  (26=msb,25=lsb)
  pinMode(27,OUTPUT); pinMode(28,OUTPUT); pinMode(14,OUTPUT); // green (14=msb,28,27=lsb)
  pinMode(15,OUTPUT); pinMode(29,OUTPUT); pinMode(11,OUTPUT); // red   (11=msb,29,15=lsb)

  REG_PIOD_OWER= 0xff;
  REG_PMC_PCER0= 1<<27;  
  REG_PIOB_PDR = 1<<25; 
  REG_PIOB_ABSR= 1<<25; 
  REG_TC0_WPMR = 0x54494D00; 
  REG_TC0_CMR0 = 0b00000000000010011100010000000000;
//  REG_TC0_CMR0 = 0b00000000000001101100010000000000; // this inverts hsync
  REG_TC0_RC0  = 1334; 
  REG_TC0_RA0  = 1174;  
  REG_TC0_CCR0 = 0b101;    
  REG_TC0_IER0 = 0b00010000; 
  REG_TC0_IDR0 = 0b11101111; 
  NVIC_EnableIRQ(TC0_IRQn);
  
  initGrid();
  drawGrid(); 
}


void loop(){
  
  runGrid();
 drawGrid();
 generations++;
 if (generations > MAX_GENERATIONS) {
 generations = 0;
 initGrid();
 }

}

void initGrid()
{
 int i, j;
 int t;

 current_grid = 0;
 for (i = 0; i < GRID_X; i++) {
 for (j = 0; j < GRID_Y; j++) {

 if (rand()%2) {
 grid[0][i][j] = 2;
 } else {
 grid[0][i][j] = 0;
 }
 }
 }
}

void runGrid()
{
 uint8_t x, y;
 int count;
 uint8_t value = 0;
 uint8_t new_grid;

 new_grid = 1 - current_grid;
 for (y = 0; y < GRID_Y; y++) {
 for (x = 0; x < GRID_X; x++) {
 count = count_neighbours(x, y);
 if (count < 2 || count > 3) { value = 0; }
 else if (count == 3) { value = 3; }
 else { value = grid[current_grid][x][y]; }
 grid[new_grid][x][y] = value;
 }
 }
 current_grid = new_grid;
}

int count_neighbours(int x, int y)
{
 int i, j;
 int sx;
 int result = 0;

 x--;
 y--;
 for (i = 0; i < 3; i++) {
 if (y < 0 || y > (GRID_Y - 1)) { continue; }
 for (j = 0; j < 3; j++) {
 if (x < 0 || x > (GRID_X - 1)) { continue; }
 if (i==1 && j == 1) { x++; continue; }
 if (grid[current_grid][x][y]) { result++; }
 x++;
 }
 y++;
 x -= 3;
 }
 return result;
}

void drawGrid()
{
 uint8_t  x,  y;
 uint8_t cx, cy;
 uint8_t grid_next_colour = 0;
 cx = 0;
 cy = 0;
 for (y = 0; y < GRID_Y; y++) {
 cx = 0;
 for (x = 0; x < GRID_X; x++) {
 if (grid[1-current_grid][x][y] != grid[current_grid][x][y]) {
  if(grid[current_grid][x][y]) {
    fb[cy][cx]=rand()%255;
 }
    else { 
    fb[cy][cx]=0;
 }

 }
 cx += 2;
 }
 cy += 2;
}
}

JLS1:
Simple Game of Life example :slight_smile:

Nice work :smiley: (I accidentally left it running all night, it's still going strong!)

Here's a quick status update for the VGA output project. Basically to get any further with this I need to learn ARM inline assembler, which is what I've been doing. With the code above the output is shimmering slightly and if you look closely some of the pixels are bigger than others. I do have code which solves the wide pixels problem and reduces the shimmering, however it is unreliable (sometimes it doesn't compile, sometimes it compiles but doesn't work, sometimes it works perfectly). I guess I have a little more to learn.

I haven't started on 640x480 1bpp yet but I still think it is viable. I don't know about 800x600 (the pixel rate would be 40Mpixels/sec) but there might be a way...

Finally when reading the datasheet I noticed that the SAM3X has some DMA controllers. I am strongly tempted to evaluate whether I can use them rather than the inline assembler. This is even more to learn (I've never used DMA before) but if it does work it would be a much better solution. It would use much less CPU for one thing :grin:

Simple Wolfram 1D Cellular Automata example :slight_smile:

/* Wolfram Cellular Automata 1D v1.0 */

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
byte fb[240][320];

#define do20(x) x x x x x x x x x x x x x x x x x x x x
#define do80(x)  do20(x) do20(x) do20(x) do20(x) 
#define do320(x) do80(x) do80(x) do80(x) do80(x) 
#define MNOP(x) asm volatile (" .rept " #x "\n\t nop \n\t .endr \n\t")

void TC0_Handler()
{
    long dummy=REG_TC0_SR0; 
                           
    if(line < 480){        
        byte * p=fb[line>>1];
        MNOP(160);
        do320(REG_PIOD_ODSR = *p++;MNOP(2);)
        REG_PIOD_ODSR = 0;
    }      
    if(line==490) digitalWriteDirect(3,1); //or digitalWriteDirect(3,0); to invert vsync
    if(line==492) digitalWriteDirect(3,0); //or digitalWriteDirect(3,1); to invert vsync
    
    line++; if(line == 525) line=0;
}

void setup(){
  
  pinMode(3,OUTPUT);  pinMode(2,OUTPUT);                      // vsync=3 hsync=2
  pinMode(25,OUTPUT); pinMode(26,OUTPUT);                     // blue  (26=msb,25=lsb)
  pinMode(27,OUTPUT); pinMode(28,OUTPUT); pinMode(14,OUTPUT); // green (14=msb,28,27=lsb)
  pinMode(15,OUTPUT); pinMode(29,OUTPUT); pinMode(11,OUTPUT); // red   (11=msb,29,15=lsb)

  REG_PIOD_OWER= 0xff;
  REG_PMC_PCER0= 1<<27;  
  REG_PIOB_PDR = 1<<25; 
  REG_PIOB_ABSR= 1<<25; 
  REG_TC0_WPMR = 0x54494D00; 
  REG_TC0_CMR0 = 0b00000000000010011100010000000000;
//  REG_TC0_CMR0 = 0b00000000000001101100010000000000; // this inverts hsync
  REG_TC0_RC0  = 1334; 
  REG_TC0_RA0  = 1174;  
  REG_TC0_CCR0 = 0b101;    
  REG_TC0_IER0 = 0b00010000; 
  REG_TC0_IDR0 = 0b11101111; 
  NVIC_EnableIRQ(TC0_IRQn);
  
}

void loop(){
  
  int iteration = 240;
  int lenght = 320;
  int row = 0;
  int state[lenght];
  int newstate[lenght];
  int i,j,k;
  int rules[8] = {
  int (rand()%2),
  int (rand()%2),
  int (rand()%2),
  int (rand()%2),
  int (rand()%2),
  int (rand()%2),
  int (rand()%2),
  int (rand()%2)};

  for (i=0;i<lenght;i++) state[i]= int (rand()%2);

   for (i=0;i<iteration;i++) {

      for (j=0;j<lenght;j++)
         newstate[j] = 0;

      for (j=0;j<lenght;j++) {
         k = 4*state[(j-1+lenght)%lenght] + 2*state[j] + state[(j+1)%lenght];
         newstate[j] = rules[k];
      }

      for (j=0;j<lenght;j++)
        state[j] = newstate[j];
        DisplayState(state,lenght,row);
        row = row + 1;
   }
}

void DisplayState(int s[],int len, int row)
{
   int i;

   for (i=0;i<len;i++) {
      if (s[i] == 1){
       fb[row][i]=255;
       }        
      else{
       fb[row][i]=0;
        }
      }
}

Chua chaotic oscillator example :slight_smile:

/* Chua Chaotic Oscillator v1.0 */

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
byte fb[240][320];

#define do20(x) x x x x x x x x x x x x x x x x x x x x
#define do80(x)  do20(x) do20(x) do20(x) do20(x) 
#define do320(x) do80(x) do80(x) do80(x) do80(x) 
#define MNOP(x) asm volatile (" .rept " #x "\n\t nop \n\t .endr \n\t")

void TC0_Handler()
{
    long dummy=REG_TC0_SR0; 
                           
    if(line < 480){        
        byte * p=fb[line>>1];
        MNOP(160);
        do320(REG_PIOD_ODSR = *p++;MNOP(2);)
        REG_PIOD_ODSR = 0;
    }      
    if(line==490) digitalWriteDirect(3,1); //or digitalWriteDirect(3,0); to invert vsync
    if(line==492) digitalWriteDirect(3,0); //or digitalWriteDirect(3,1); to invert vsync
    
    line++; if(line == 525) line=0;
}

void setup(){
  
  pinMode(3,OUTPUT);  pinMode(2,OUTPUT);                      // vsync=3 hsync=2
  pinMode(25,OUTPUT); pinMode(26,OUTPUT);                     // blue  (26=msb,25=lsb)
  pinMode(27,OUTPUT); pinMode(28,OUTPUT); pinMode(14,OUTPUT); // green (14=msb,28,27=lsb)
  pinMode(15,OUTPUT); pinMode(29,OUTPUT); pinMode(11,OUTPUT); // red   (11=msb,29,15=lsb)

  REG_PIOD_OWER= 0xff;
  REG_PMC_PCER0= 1<<27;  
  REG_PIOB_PDR = 1<<25; 
  REG_PIOB_ABSR= 1<<25; 
  REG_TC0_WPMR = 0x54494D00; 
  REG_TC0_CMR0 = 0b00000000000010011100010000000000;
//  REG_TC0_CMR0 = 0b00000000000001101100010000000000; // this inverts hsync
  REG_TC0_RC0  = 1334; 
  REG_TC0_RA0  = 1174;  
  REG_TC0_CCR0 = 0b101;    
  REG_TC0_IER0 = 0b00010000; 
  REG_TC0_IDR0 = 0b11101111; 
  NVIC_EnableIRQ(TC0_IRQn);
  
}

  float x = 0.5;
  float y = 0.25;
  float z = 0.125;
  float h = 0;
       
  float dt = 0.005;
 
  float alpha = 15.6;
  float beta = 28.58;
  float a = -1.14286;
  float b = -0.714286;

void loop(){
  
  float oldx = x;
  float oldy = y;
  float oldz = z;

  h = (b * x) + (0.5 * (a - b) * (abs(x+1) - abs(x-1)));
               
  x = oldx + dt * (alpha * (oldy - oldx - h));
  y = oldy + dt * (oldx - oldy + oldz);
  z = oldz + dt * (-beta * oldy);

  fb[120+int(65*4*y)][160+int(65*x)]=255;

}

Nice :slight_smile: It looks great in colour too, change the last line to this:

  static int c=0;
  fb[120+int(65*4*y)][160+int(65*x)]=(c++)>>12;

stimmer do you think your code can be packaged up as a library?
it would make it very simple to make nice graphics apps with the Due

m

Yes, but please not just yet. I am still evaluating better ways of generating the VGA signal, which may be more steady and use less CPU. This might mean changing the pins the signal is output on. However I am not likely to change the format of the framebuffer (240x320 array) so any nice demo code people make now should still work and would be easy to use as example code in a library if authors wanted.

There's also some design decisions to make - if it turns out 640x480 works, should it be a separate library or the same one? Being the same library could make code larger and slower, but might allow mode changes at runtime. Also a library would need standard graphics functions (lines, text, bitmaps) - this is different in an 8bpp mode to a 1bpp mode. And whether to support composite output - 320x200 or 240 8-bit greyscale would be possible. I think that a single library would be more easily maintainable.

Attached are some screenshots of JLS1's demo programs. Chua oscillator has my modification for colour. Life is Conway's famous 'game' we all know and love.

Cellular automata are explained in Wolfram's 'A New Kind Of Science' chapter 6 - he classifies them into four main types (uniform, structured, random, and complex) and others which are rarer and hard to classify. Attached are two examples, cell2 is complex and cell1 is one that doesn't fit the main types.

Mira-Gumowski Fractal nice colour example :slight_smile:

/* Mira Gumowski Fractal v1.0 */

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
byte fb[240][320];

#define do20(x) x x x x x x x x x x x x x x x x x x x x
#define do80(x)  do20(x) do20(x) do20(x) do20(x) 
#define do320(x) do80(x) do80(x) do80(x) do80(x) 
#define MNOP(x) asm volatile (" .rept " #x "\n\t nop \n\t .endr \n\t")

void TC0_Handler()
{
    long dummy=REG_TC0_SR0; 
                           
    if(line < 480){        
        byte * p=fb[line>>1];
        MNOP(160);
        do320(REG_PIOD_ODSR = *p++;MNOP(2);)
        REG_PIOD_ODSR = 0;
    }      
    if(line==490) digitalWriteDirect(3,1); //or digitalWriteDirect(3,0); to invert vsync
    if(line==492) digitalWriteDirect(3,0); //or digitalWriteDirect(3,1); to invert vsync
    
    line++; if(line == 525) line=0;
}

void setup(){
  
  pinMode(3,OUTPUT);  pinMode(2,OUTPUT);                      // vsync=3 hsync=2
  pinMode(25,OUTPUT); pinMode(26,OUTPUT);                     // blue  (26=msb,25=lsb)
  pinMode(27,OUTPUT); pinMode(28,OUTPUT); pinMode(14,OUTPUT); // green (14=msb,28,27=lsb)
  pinMode(15,OUTPUT); pinMode(29,OUTPUT); pinMode(11,OUTPUT); // red   (11=msb,29,15=lsb)

  REG_PIOD_OWER= 0xff;
  REG_PMC_PCER0= 1<<27;  
  REG_PIOB_PDR = 1<<25; 
  REG_PIOB_ABSR= 1<<25; 
  REG_TC0_WPMR = 0x54494D00; 
  REG_TC0_CMR0 = 0b00000000000010011100010000000000;
//  REG_TC0_CMR0 = 0b00000000000001101100010000000000; // this inverts hsync
  REG_TC0_RC0  = 1334; 
  REG_TC0_RA0  = 1174;  
  REG_TC0_CCR0 = 0b101;    
  REG_TC0_IER0 = 0b00010000; 
  REG_TC0_IDR0 = 0b11101111; 
  NVIC_EnableIRQ(TC0_IRQn);
  
}

   float a = -0.6624;
   float c = 2*(1-a);
   float x = 14.7176;
   float y = -14.6602;
   float f = 0;
   
   int xx,yy;
   
   int cnt = 0;
   int col = 255;

void loop(){
  
    double oldx = x;
    x = f+1*y;
    f = a*x+c*x*x/(1+x*x);
    y = f-oldx;
    
    xx = 160+3*x;
    yy = 120+4*y;
    
    if (cnt == 2000) {
	col = rand();
	cnt = 0; }
		
    cnt++; 
    
    if ((xx>=0)&(xx<=319)&(yy>=0)&(yy<=239)) {
		
  		fb[yy][xx]=col; }

}

Hopalong Orbit Fractal example :slight_smile:

/* Hopalong Orbit Fractal v1.0 */

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
byte fb[240][320];

#define do20(x) x x x x x x x x x x x x x x x x x x x x
#define do80(x)  do20(x) do20(x) do20(x) do20(x) 
#define do320(x) do80(x) do80(x) do80(x) do80(x) 
#define MNOP(x) asm volatile (" .rept " #x "\n\t nop \n\t .endr \n\t")

void TC0_Handler()
{
    long dummy=REG_TC0_SR0; 
                           
    if(line < 480){        
        byte * p=fb[line>>1];
        MNOP(160);
        do320(REG_PIOD_ODSR = *p++;MNOP(2);)
        REG_PIOD_ODSR = 0;
    }      
    if(line==490) digitalWriteDirect(3,1); //or digitalWriteDirect(3,0); to invert vsync
    if(line==492) digitalWriteDirect(3,0); //or digitalWriteDirect(3,1); to invert vsync
    
    line++; if(line == 525) line=0;
}

void setup(){
  
  pinMode(3,OUTPUT);  pinMode(2,OUTPUT);                      // vsync=3 hsync=2
  pinMode(25,OUTPUT); pinMode(26,OUTPUT);                     // blue  (26=msb,25=lsb)
  pinMode(27,OUTPUT); pinMode(28,OUTPUT); pinMode(14,OUTPUT); // green (14=msb,28,27=lsb)
  pinMode(15,OUTPUT); pinMode(29,OUTPUT); pinMode(11,OUTPUT); // red   (11=msb,29,15=lsb)

  REG_PIOD_OWER= 0xff;
  REG_PMC_PCER0= 1<<27;  
  REG_PIOB_PDR = 1<<25; 
  REG_PIOB_ABSR= 1<<25; 
  REG_TC0_WPMR = 0x54494D00; 
  REG_TC0_CMR0 = 0b00000000000010011100010000000000;
//  REG_TC0_CMR0 = 0b00000000000001101100010000000000; // this inverts hsync
  REG_TC0_RC0  = 1334; 
  REG_TC0_RA0  = 1174;  
  REG_TC0_CCR0 = 0b101;    
  REG_TC0_IER0 = 0b00010000; 
  REG_TC0_IDR0 = 0b11101111; 
  NVIC_EnableIRQ(TC0_IRQn);
  
}

   float a = 0.5;
   float b = -0.6;
   float c = 0.7;
   float x = 1;
   float y = 0;
   
   int xx,yy;
   
   int cnt = 0;
   int col = 255;

void loop(){
  
    double oldx = x;
    double oldy = y;
    
    x = oldy-(oldx/abs(oldx))*sqrt(abs(b*oldx-c));
    y = a-oldx;
    
    xx = 160+8*x;
    yy = 120+8*y;
    
    if (cnt == 2000) {
	col = rand();
	cnt = 0; }
		
    cnt++; 
    
    if ((xx>=0)&(xx<=319)&(yy>=0)&(yy<=239)) {
		
  		fb[yy][xx]=col; }

}

This is the latest version of the code. The output pins have changed completely and are now more convenient. Also I have attempted to reduce/eliminate the shimmering problem.

The connections are now:

Due pin 34 -> 820R resistor -> VGA pin 3 (blue)
Due pin 35 -> 390R resistor -> VGA pin 3 (blue)

Due pin 36 -> 2k2 resistor -> VGA pin 2 (green)
Due pin 37 -> 1k resistor -> VGA pin 2 (green)
Due pin 38 -> 470R resistor -> VGA pin 2 (green)

Due pin 39 -> 2k2 resistor -> VGA pin 1 (red)
Due pin 40 -> 1k resistor -> VGA pin 1 (red)
Due pin 41 -> 470R resistor -> VGA pin 1(red)

Due pin 42 -> VGA pin 14 (VSync)
Due pin 43 -> VGA pin 13 (HSync)

Due pin GND -> VGA pins 5,6,7,8,10

/* Arduino Due VGA-Out v0.8 by stimmer
http://arduino.cc/forum/index.php/topic,130742.0.html */

int bins[256];
void  __attribute__((aligned(64))) doLine(byte *p){
        asm volatile (
        "isb \n\t"
        "dsb \n\t"        
        "movw r1,#0x4254 \n\t"
        "movt r1,#0x4009 \n\t"
        "ldr r0,[r1] \n\t"
        "subs r0,#66 \n\t"
        "lsls r0,r0,#1 \n\t"
        "orr r0,#1 \n\t"  
        "add r0,pc \n\t"
        "isb \n\t"
        ".align 5 \n\t"
        "bx r0 \n\t"
        ".rept 160 \n\t"
        "nop \n\t"
        ".endr \n\t"
        
        //this is debugging code, but because the timimg is so sensitive I have to leave it in
        "ldr r0,[r1] \n\t"
        "subs r0,#128 \n\t"
        "ldr r2,[%[bins],r0,lsl #2] \n\t"
        "adds r2,#1 \n\t"
        "str r2,[%[bins],r0,lsl #2] \n\t" 
        
        
        "movw r1,#0x1238 \n\t"
        "movt r1,#0x400e \n\t"
        
        ".rept 320 \n\t"        
        "ldrb r0,[%[bytes]],#1 \n\t"
        "lsls r0,r0,#2 \n\t"
        "str r0,[r1] \n\t"
        ".endr \n\t"
        
        "mov r0,#0 \n\t"
        "str r0,[r1] \n\t"
        :[bytes]"+r"(p)
        :[bins]"r"(bins)
        :"r0","r1","r2"
        );  
}

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
byte fb[240][320];

void PWM_Handler()
{
    long dummy=REG_PWM_ISR1; 
                           
    if(line < 480){        
        byte * p=fb[line>>1];
        doLine(p);
    }      
    if(line==490) digitalWriteDirect(42,1); //or digitalWriteDirect(42,0); to invert vsync
    if(line==492) digitalWriteDirect(42,0); //or digitalWriteDirect(42,1); 

    line++; if(line == 525) line=0;
}

void setup(){
    
  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);

}

void loop(){
  
  for(int x=0;x<320;x++){
    int my=240;
    for(float Z=-15.02;Z<15.0;Z+=0.01){
      float X=(x-159.98)/16.0;
      float R=sqrt(X*X+Z*Z);
      float Y=150.0*(1.0+sin(R)/R);
      int y=320.0-(Z*4.0)-Y;
      if(y<my && y>=0){
        byte c=Y;
        fb[y][x]=c;
        my=y;
      }
    }
  }
           
}

I have had some success with 640x480 1bpp. This version is using SPI and DMA - this has the advantage that now the CPU can carry on working most of the time while the screen is being drawn. Therefore it is much faster than the 320x240x8bpp code. The downside is that the video output is on the SPI connector (MOSI - middle pin of lower row - look at http://arduino.cc/forum/index.php/topic,132130.0.html if you can't find it). Connect MOSI through a 100R resistor to VGA R,G and B. VSync is on pin 42 and HSync is on pin 43.

There is slight shimmering in this version which I hope to be able to correct. Also the timings are quite a way out (pixel clock is 10% too fast) hopefully most LCDs will be able to stretch the picture enough. If anyone does try it and it does/doesn't work please tell me.

If you make any demos with this please use the putPixel/getPixel functions if they are fast enough, as I might yet change the underlying format of the framebuffer.

/* Arduino Due VGA-Out 640x480 v0.4 by stimmer
http://arduino.cc/forum/index.php/topic,130742.0.html */

int bins[256];
void  __attribute__((aligned(64))) doLine(byte *p){
        asm volatile (        
        "isb \n\t"
        "dsb \n\t" 
        "movw r1,#0x4254 \n\t"
        "movt r1,#0x4009 \n\t"
        "ldr r0,[r1] \n\t"
        "subs r0,#35 \n\t"
        "lsls r0,r0,#1 \n\t"
        "orr r0,#1 \n\t"  
        "add r0,pc \n\t"
        "isb \n\t"
        ".align 5 \n\t"
        "bx r0 \n\t"
        ".rept 200 \n\t"
        "nop \n\t"
        ".endr \n\t"
                
        //this is debugging code, but because the timimg is so sensitive I have to leave it in
        "ldr r0,[r1] \n\t"
        "subs r0,#128 \n\t"
        "ldr r2,[%[bins],r0,lsl #2] \n\t"
        "adds r2,#1 \n\t"
        "str r2,[%[bins],r0,lsl #2] \n\t" 
        
        "movw r1,#0x4104 \n\t"
        "movt r1,#0x400C \n\t"
        "str %[bytes],[r1] \n\t"
        "movw r1,#0x4110 \n\t"
        "movt r1,#0x400C \n\t"
        "movw r0,80 \n\t"
        "movt r0,#0x0000 \n\t"
        "str r0,[r1] \n\t"
        "movw r1,#0x4028 \n\t"
        "movt r1,#0x400C \n\t"
        "mov r0,1<<5 \n\t"
        "str r0,[r1] \n\t"
        "movw r1,#0x0e04 \n\t"
        "movt r1,#0x400e \n\t"
        "mov r0,1<<26 \n\t"
        ".rept 26\n\t nop\n\t .endr\n\t"
        "str r0,[r1] \n\t"
              
        :[bytes]"+r"(p)
        :[bins]"r"(bins)
        :"r0","r1","r2"
        );  
          
}

void __attribute__((aligned(64))) DMAC_Handler()
{
    REG_SPI0_TDR=0;
    asm volatile("nop\n\t nop\n\t nop\n\t");
    REG_PIOA_PER  = 1<<26; 
    uint32_t dummy=REG_DMAC_EBCISR;
}

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
byte fb[480][80] __attribute__((aligned(256)));

void __attribute__((aligned(64))) PWM_Handler()
{
    long dummy=REG_PWM_ISR1; 
                 
    if(line < 480){        
        byte * p=fb[line];
        doLine(p);
    }      
    if(line==490) digitalWriteDirect(42,1); //or digitalWriteDirect(42,0); to invert vsync
    if(line==492) digitalWriteDirect(42,0); //or digitalWriteDirect(42,1); 
    
    line++; if(line == 525){
      line=0;
    }
}

void setup(){
    
  pinMode(42,OUTPUT); pinMode(43,OUTPUT);                     // vsync=42 hsync=43
  // video output is on SPI MOSI pin (bottom middle on SAM3 3x2 SPI connector)                                      
  
  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);

  REG_PMC_PCER1= 1<<7;  
  REG_DMAC_WPMR=DMAC_WPMR_WPKEY(0x444d4143);
  REG_DMAC_EN=1;
  REG_DMAC_GCFG=0x00;
  REG_DMAC_EBCIER=1<<5;
  REG_DMAC_DADDR5=(uint32_t)&REG_SPI0_TDR;  
  REG_DMAC_DSCR5=0;
  REG_DMAC_CTRLB5=0x20310000;
  REG_DMAC_CFG5=  0x21012210;
    
  NVIC_EnableIRQ(DMAC_IRQn);

  REG_PIOA_PDR  = (1<<25)|(1<<27)|(1<<28);
  REG_PIOA_PER  = 1<<26;
  REG_PIOA_ABSR&=~((1<<25)|(1<<27)|(1<<28));
  REG_PMC_PCER0= 1<<24;
  REG_SPI0_WPMR=0x53504900; 
  REG_SPI0_CR=0x1;
  REG_SPI0_MR=0x00000011;
  SPI0->SPI_CSR[0]=0x00000300; 
  
}

inline void putPixel(int x,int y,byte c=1){
  if((x<0)||(x>=640)||(y<0)||(y>=480))return;
  byte mask=1<<((~x)&7);
  if(c) fb[y][x>>3]|= mask;
  else  fb[y][x>>3]&=~mask;
}

inline int getPixel(int x,int y){
  if((x<0)||(x>=640)||(y<0)||(y>=480))return -1;
  byte mask=1<<((~x)&7);
  return (fb[y][x>>3] & mask)?1:0;
}

inline void clearBuffer(byte c=0){
  memset(fb,c?255:0,sizeof(fb));  
}

void loop(){

  float c[3][2]={{random(640),random(480)},{random(640),random(480)},{random(640),random(480)}};
  
  static int j=1;
  if(random(10)==0){clearBuffer(j);j=1-j;}
 
  for(int x=0;x<640;x+=2){putPixel(x,0);putPixel(x,479);}
  for(int y=0;y<480;y+=2){putPixel(0,y);putPixel(639,y);}

  float x=c[0][0],y=c[0][1];
 
  for(int t=0;t<40000;t++){
    int i=random(0,3);
    x=(x+c[i][0])/2;
    y=(y+c[i][1])/2;
    putPixel(x,y,j);
  }
           
}

Great news - SVGA 800x600x1bpp is possible :grin: Connections are the same as 640x480 above (Video out on SPI MOSI, VSync on pin 42 and HSync on pin 43)

/* Arduino Due SVGA-Out 800x600 v0.3 by stimmer
http://arduino.cc/forum/index.php/topic,130742.0.html 
Please do not attempt to understand this code :-) */

int bins[256];
void  __attribute__((aligned(64))) doLine(byte *p){
        asm volatile (        
        "isb \n\t"
        "dsb \n\t" 
        "movw r1,#0x4254 \n\t"
        "movt r1,#0x4009 \n\t"
        "ldr r0,[r1] \n\t"
        "subs r0,#40 \n\t"
        "lsls r0,r0,#1 \n\t"
        "orr r0,#1 \n\t"  
        "add r0,pc \n\t"
        "isb \n\t"
        ".align 5 \n\t"
        "bx r0 \n\t"
        ".rept 90 \n\t"
        "nop \n\t"
        ".endr \n\t"
                
        //this is debugging code, but because the timimg is so sensitive I have to leave it in
        "ldr r0,[r1] \n\t"
        "subs r0,#128 \n\t"
        "ldr r2,[%[bins],r0,lsl #2] \n\t"
        "adds r2,#1 \n\t"
        "str r2,[%[bins],r0,lsl #2] \n\t" 
        
        //"movw r1,#0x40DC \n\t"
        //"movt r1,#0x400C \n\t"
        //"str %[bytes],[r1] \n\t"
        "movw r1,#0x40E8 \n\t"
        "movt r1,#0x400C \n\t"
        "movw r0,26 \n\t"
        "movt r0,#0x1203 \n\t"
        "str r0,[r1] \n\t"
        "movw r1,#0x4028 \n\t"
        "movt r1,#0x400C \n\t"
        "mov r0,1<<4 \n\t"
        "str r0,[r1] \n\t"
        "movw r1,#0x0e04 \n\t"
        "movt r1,#0x400e \n\t"
        "mov r0,1<<26 \n\t"
        ".rept 12\n\t nop\n\t .endr\n\t"
        "str r0,[r1] \n\t"
              
        :[bytes]"+r"(p)
        :[bins]"r"(bins)
        :"r0","r1","r2"
        );  
          
}

void __attribute__((aligned(64))) DMAC_Handler()
{
    REG_PIOA_PER  = 1<<26;     
    uint32_t dummy=REG_DMAC_EBCISR;
}

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
uint16_t fb[600][52] __attribute__((aligned(256)));

void __attribute__((aligned(64))) PWM_Handler()
{
    long dummy=REG_PWM_ISR1; 
                 
    if(line < 600){        
        //byte * p=fb[line];
        doLine(0);
    }      
    if(line==601) digitalWriteDirect(42,1); //or digitalWriteDirect(42,0); to invert vsync
    if(line==605) digitalWriteDirect(42,0); //or digitalWriteDirect(42,1); 
    
    line++; if(line == 628){
      REG_DMAC_SADDR4=(uint32_t)fb;
      line=0;
    }
}

void setup(){
    
  pinMode(42,OUTPUT); pinMode(43,OUTPUT);                     // vsync=42 hsync=43
  // video output is on SPI MOSI pin (bottom middle on SAM3 3x2 SPI connector)                                      
  
  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=2218;
  REG_PWM_CDTY2=1949;
  REG_PWM_SCM=0;
  REG_PWM_IER1=1<<2;
  REG_PWM_ENA= 1<<2;  
  NVIC_EnableIRQ(PWM_IRQn);

  REG_PMC_PCER1= 1<<7;  
  REG_DMAC_WPMR=DMAC_WPMR_WPKEY(0x444d4143);
  REG_DMAC_EN=1;
  REG_DMAC_GCFG=0x00;
  REG_DMAC_EBCIER=1<<4;
  REG_DMAC_DADDR4=(uint32_t)&REG_SPI0_TDR;  
  REG_DMAC_DSCR4=0;
  REG_DMAC_CTRLB4=0x20310000;
  REG_DMAC_CFG4=  0x01412210;
    
  NVIC_EnableIRQ(DMAC_IRQn);

  REG_PIOA_PDR  = (1<<25)|(1<<27)|(1<<28);
  REG_PIOA_PER  = 1<<26;
  REG_PIOA_ABSR&=~((1<<25)|(1<<27)|(1<<28));
  REG_PMC_PCER0= 1<<24;
  REG_SPI0_WPMR=0x53504900; 
  REG_SPI0_CR=0x1;
  REG_SPI0_MR=0x00000011;
  SPI0->SPI_CSR[0]=0x00000280; 
  
  Serial.begin(9600);
}

inline void putPixel(int x,int y,byte c=1){
  if((x<0)||(x>=800)||(y<0)||(y>=600))return;
  uint16_t mask=1<<((~x)&15);
  if(c) fb[y][x>>4]|= mask;
  else  fb[y][x>>4]&=~mask;
}

inline int getPixel(int x,int y){
  if((x<0)||(x>=800)||(y<0)||(y>=600))return -1;
  uint16_t mask=1<<((~x)&15);
  return (fb[y][x>>4] & mask)?1:0;
}

#define sgn(x) (((x)>0)?1:((x)<0)?-1:0)
void drawLine(int x0, int y0, int x1, int y1,int c){
   int dx=abs(x1-x0), dy=abs(y1-y0),sx=sgn(x1-x0),sy=sgn(y1-y0);
   int err=dx-dy; 
   do{ putPixel(x0,y0,c<2?c:!getPixel(x0,y0));
       int e2=2*err;
       if (e2 > -dy){err-=dy;x0+=sx;}
       if (e2 <  dx){err+=dx;y0+=sy;}
   }   while ((x0!=x1)||(y0!=y1));
}

void loop(){  
  int j=random(6)-221;
  for(int i=j;i<800-j;i++)drawLine(i,j-100,799-i,699-j,2);
  for(int i=799-j;i>=j;i--)drawLine(j,i-100,799-j,699-i,2);             
}

Hi Stimmer

800x600 is great work many thanks :slight_smile:

Kamil

P.S. is possible 640x480 with some colors ?

SVGA Hopalong Orbit Fractal :slight_smile:

/* Hopalong Orbit Fractal v1.1 by JLS 2012 */

/* Arduino Due SVGA-Out 800x600 v0.3 by stimmer
http://arduino.cc/forum/index.php/topic,130742.0.html 
Please do not attempt to understand this code :-) */

int bins[256];
void  __attribute__((aligned(64))) doLine(byte *p){
        asm volatile (        
        "isb \n\t"
        "dsb \n\t" 
        "movw r1,#0x4254 \n\t"
        "movt r1,#0x4009 \n\t"
        "ldr r0,[r1] \n\t"
        "subs r0,#40 \n\t"
        "lsls r0,r0,#1 \n\t"
        "orr r0,#1 \n\t"  
        "add r0,pc \n\t"
        "isb \n\t"
        ".align 5 \n\t"
        "bx r0 \n\t"
        ".rept 90 \n\t"
        "nop \n\t"
        ".endr \n\t"
                
        //this is debugging code, but because the timimg is so sensitive I have to leave it in
        "ldr r0,[r1] \n\t"
        "subs r0,#128 \n\t"
        "ldr r2,[%[bins],r0,lsl #2] \n\t"
        "adds r2,#1 \n\t"
        "str r2,[%[bins],r0,lsl #2] \n\t" 
        
        //"movw r1,#0x40DC \n\t"
        //"movt r1,#0x400C \n\t"
        //"str %[bytes],[r1] \n\t"
        "movw r1,#0x40E8 \n\t"
        "movt r1,#0x400C \n\t"
        "movw r0,26 \n\t"
        "movt r0,#0x1203 \n\t"
        "str r0,[r1] \n\t"
        "movw r1,#0x4028 \n\t"
        "movt r1,#0x400C \n\t"
        "mov r0,1<<4 \n\t"
        "str r0,[r1] \n\t"
        "movw r1,#0x0e04 \n\t"
        "movt r1,#0x400e \n\t"
        "mov r0,1<<26 \n\t"
        ".rept 12\n\t nop\n\t .endr\n\t"
        "str r0,[r1] \n\t"
              
        :[bytes]"+r"(p)
        :[bins]"r"(bins)
        :"r0","r1","r2"
        );  
          
}

void __attribute__((aligned(64))) DMAC_Handler()
{
    REG_PIOA_PER  = 1<<26;     
    uint32_t dummy=REG_DMAC_EBCISR;
}

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

volatile short line;
uint16_t fb[600][52] __attribute__((aligned(256)));

void __attribute__((aligned(64))) PWM_Handler()
{
    long dummy=REG_PWM_ISR1; 
                 
    if(line < 600){        
        //byte * p=fb[line];
        doLine(0);
    }      
    if(line==601) digitalWriteDirect(42,1); //or digitalWriteDirect(42,0); to invert vsync
    if(line==605) digitalWriteDirect(42,0); //or digitalWriteDirect(42,1); 
    
    line++; if(line == 628){
      REG_DMAC_SADDR4=(uint32_t)fb;
      line=0;
    }
}

void setup(){
    
  pinMode(42,OUTPUT); pinMode(43,OUTPUT);                     // vsync=42 hsync=43
  // video output is on SPI MOSI pin (bottom middle on SAM3 3x2 SPI connector)                                      
  
  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=2218;
  REG_PWM_CDTY2=1949;
  REG_PWM_SCM=0;
  REG_PWM_IER1=1<<2;
  REG_PWM_ENA= 1<<2;  
  NVIC_EnableIRQ(PWM_IRQn);

  REG_PMC_PCER1= 1<<7;  
  REG_DMAC_WPMR=DMAC_WPMR_WPKEY(0x444d4143);
  REG_DMAC_EN=1;
  REG_DMAC_GCFG=0x00;
  REG_DMAC_EBCIER=1<<4;
  REG_DMAC_DADDR4=(uint32_t)&REG_SPI0_TDR;  
  REG_DMAC_DSCR4=0;
  REG_DMAC_CTRLB4=0x20310000;
  REG_DMAC_CFG4=  0x01412210;
    
  NVIC_EnableIRQ(DMAC_IRQn);

  REG_PIOA_PDR  = (1<<25)|(1<<27)|(1<<28);
  REG_PIOA_PER  = 1<<26;
  REG_PIOA_ABSR&=~((1<<25)|(1<<27)|(1<<28));
  REG_PMC_PCER0= 1<<24;
  REG_SPI0_WPMR=0x53504900; 
  REG_SPI0_CR=0x1;
  REG_SPI0_MR=0x00000011;
  SPI0->SPI_CSR[0]=0x00000280; 
  
  Serial.begin(9600);
}

inline void putPixel(int x,int y,byte c=1){
  if((x<0)||(x>=800)||(y<0)||(y>=600))return;
  uint16_t mask=1<<((~x)&15);
  if(c) fb[y][x>>4]|= mask;
  else  fb[y][x>>4]&=~mask;
}

inline int getPixel(int x,int y){
  if((x<0)||(x>=800)||(y<0)||(y>=600))return -1;
  uint16_t mask=1<<((~x)&15);
  return (fb[y][x>>4] & mask)?1:0;
}

#define sgn(x) (((x)>0)?1:((x)<0)?-1:0)
void drawLine(int x0, int y0, int x1, int y1,int c){
   int dx=abs(x1-x0), dy=abs(y1-y0),sx=sgn(x1-x0),sy=sgn(y1-y0);
   int err=dx-dy; 
   do{ putPixel(x0,y0,c<2?c:!getPixel(x0,y0));
       int e2=2*err;
       if (e2 > -dy){err-=dy;x0+=sx;}
       if (e2 <  dx){err+=dx;y0+=sy;}
   }   while ((x0!=x1)||(y0!=y1));
}

   float a = 0.5;
   float b = -0.6;
   float c = 0.7;
   float x = 1;
   float y = 0;
   
   int xx,yy;
   
   int cnt = 0;
   int col = 1;

void loop(){  
         
    double oldx = x;
    double oldy = y;
    
    x = oldy-(oldx/abs(oldx))*sqrt(abs(b*oldx-c));
    y = a-oldx;
    
    xx = 400+10*x;
    yy = 300+10*y;
    
    if (cnt == 1000) {
	col = rand()%2;
	cnt = 0; }
		
    cnt++; 
    
    if ((xx>=0)&(xx<=799)&(yy>=0)&(yy<=599)) putPixel(xx,yy,col);
}