mortonkopf
Well-known member
Hi all, I am working on a project, for which a 'by product' was getting a solid VGA RGB output from Teensy 3.1. I managed to get something up and running by using the work done by Lukas Hartmann, and greatly simplified his code to be left with the bare bones of getting a signal up and running:
As you can see, its a simple set up and the code below only outputs from an array filled in the loop. It is 120 by 100 pixels, with padding on the timings to produce a reasonable graphic spread. I really should be looking at buffer filling from SD files and possibly using DMA, but at the moment that isn't my priority. Hopefully someone will find it useful for their projects.
The code uses port manipulation on port B to get the pins changing quickly enough, with resistors on the outputs to produce the 0.7v signal required for the VGA monitor. I tried a range of values, and it seems like the 270 to 470 ohm area was good. The timings are critical, and the image is easily lost with even minor changes, hence the availability of some NOP for padding.
mortonkopf
EDIT: please see corrected code in later post - A pointer is needed to recall the correct buffer values!
The timings for the screen output are:
Vertical
60 Hz frame rate
1/60 = 0.016667 Seconds period (16667 uS)
525 vertical scan lines (480 visible)
Therefore: 1 / 60 / 525 = 31.746 uS per line (13.5 KHz)
Vertical sync pulse: 2 lines ( 64 uS)
Back porch: 33 lines ( 1047 uS)
Visible area: 480 lines (15238 uS)
Front porch: 10 lines ( 317 uS)
---------------------------------------
TOTAL 16666 uS per frame
Horizontal
31.746 uS per line (13.5 KHz)
800 pixels per line (640 visible)
Therefore: 1 / 60 / 525 / 800 = 39.68 nS per pixel (25.2 MHz)
Horizontal sync pulse: 96 pixels ( 3.81 uS)
Back porch: 48 pixels ( 1.90 uS)
Visible area: 640 pixels (25.40 uS)
Front porch: 16 pixels ( 0.63 uS)
-------------------------------------------
TOTAL 31.74 uS per line
As you can see, its a simple set up and the code below only outputs from an array filled in the loop. It is 120 by 100 pixels, with padding on the timings to produce a reasonable graphic spread. I really should be looking at buffer filling from SD files and possibly using DMA, but at the moment that isn't my priority. Hopefully someone will find it useful for their projects.
The code uses port manipulation on port B to get the pins changing quickly enough, with resistors on the outputs to produce the 0.7v signal required for the VGA monitor. I tried a range of values, and it seems like the 270 to 470 ohm area was good. The timings are critical, and the image is easily lost with even minor changes, hence the availability of some NOP for padding.
mortonkopf
EDIT: please see corrected code in later post - A pointer is needed to recall the correct buffer values!
Code:
//based on timings from Lukas Hartmann
//https://www.youtube.com/watch?v=FfxH6zlsDGo
// using 72MHz Teensy 3.1
// using resistors on outputs to derive 0.7v signals
#define Width 120 //sreen pixels
#define Height 100 //screen pixels
uint8_t lcdBuffer[Height*Width];
// VGA PINS
#define RGB_OUT GPIOB_PDOR
#define PIN_VR1 16
#define PIN_VR2 17
#define PIN_VG1 19
#define PIN_VG2 18
#define PIN_VB1 0
#define PIN_VB2 1
#define PIN_VI1 32
#define PIN_VI2 25
#define PIN_VBLANK 8
#define PIN_HBLANK 7
static volatile int VSYNC = 0;
#define NOP asm volatile("nop\n\t");
volatile uint32_t currentLine = 0;
volatile uint32_t currentFrame = 0;
IntervalTimer timer0;
#define UPPER_BORDER 40
void timerLine() {
cli();
if (currentLine>1) {
digitalWrite(PIN_VBLANK, 1);
} else {
digitalWrite(PIN_VBLANK, 0);
}
digitalWrite(PIN_HBLANK, 0);
RGB_OUT = 0x00;
RGB_OUT = 0x00;
RGB_OUT = 0x00;
RGB_OUT = 0x00;
digitalWrite(PIN_HBLANK, 1);
currentLine++;
currentLine = currentLine%525;
NOP;
uint32_t y = (currentLine - UPPER_BORDER)>>2;//how many rows to duplicate (to square up the image)
//display output section is within the timer >>
if (y>=0 && y<Height){ //for each row do
for (int i=0; i<40; i++) {//left edge buffer
RGB_OUT = 0;
}
for (int x=Width; x>0; x--) {
uint32_t color = lcdBuffer[x*y];
color = color | (color<<12); //shift bits
RGB_OUT = color;
RGB_OUT = color;
RGB_OUT = color;
RGB_OUT = color; //four calls wide to square up each pixel
}//end of one row of pixels
}//end of display output section
VSYNC = 0;
if (y==80) {
VSYNC = 1;
}
// RGB_OUT = 0;
sei();
}
void setup() {
delay(500);
pinMode(PIN_VR1, OUTPUT);
pinMode(PIN_VG1, OUTPUT);
pinMode(PIN_VB1, OUTPUT);
pinMode(PIN_VI1, OUTPUT);
pinMode(PIN_VR2, OUTPUT);
pinMode(PIN_VG2, OUTPUT);
pinMode(PIN_VB2, OUTPUT);
pinMode(PIN_VI2, OUTPUT);
pinMode(PIN_VBLANK, OUTPUT);
pinMode(PIN_HBLANK, OUTPUT);
timer0.begin(timerLine, 31.468);
}
unsigned int tick = 0;
int y =0;
void loop()
{
if (VSYNC==1) {
tick++;
VSYNC=0;
//fill the buffer
for (y=20;y<1500;y++){
for (int x=0;x<(Height*Width);x++){ //this just fills the buffer with some colours
lcdBuffer[x] = x/y;
}}
for (y=1500;y>20;y--){
for (int x=0;x<(Height*Width);x++){ //this just fills the buffer with some colours
lcdBuffer[x] = x/y;
}}
////do some stuff here?////////
}
}
The timings for the screen output are:
Vertical
60 Hz frame rate
1/60 = 0.016667 Seconds period (16667 uS)
525 vertical scan lines (480 visible)
Therefore: 1 / 60 / 525 = 31.746 uS per line (13.5 KHz)
Vertical sync pulse: 2 lines ( 64 uS)
Back porch: 33 lines ( 1047 uS)
Visible area: 480 lines (15238 uS)
Front porch: 10 lines ( 317 uS)
---------------------------------------
TOTAL 16666 uS per frame
Horizontal
31.746 uS per line (13.5 KHz)
800 pixels per line (640 visible)
Therefore: 1 / 60 / 525 / 800 = 39.68 nS per pixel (25.2 MHz)
Horizontal sync pulse: 96 pixels ( 3.81 uS)
Back porch: 48 pixels ( 1.90 uS)
Visible area: 640 pixels (25.40 uS)
Front porch: 16 pixels ( 0.63 uS)
-------------------------------------------
TOTAL 31.74 uS per line
Last edited: