Hello, Im in the middle of a personal project investigating the exact operation of one of Nick Gammon's codes that is to show images to a monitor via VGA (a few days ago I created a post with the doubt about the use of "register", solved :D, thanks for that).
But I got involved in a problem that I can't solve, it would be easier to solve or at least know what the problem is if I had an oscilloscope, but one of those is out of my savings :(, so all the code and tests are theoretical and results on the monitor.
I am using this code:
#include <avr/pgmspace.h>
#include <avr/sleep.h>
const byte hSyncPin = 3; // <------- HSYNC
const byte redPin = 4; // <------- Red pixel data
const byte greenPin = 5; // <------- Green pixel data
const byte bluePin = 6; // <------- Blue pixel data
const byte vSyncPin = 10; // <------- VSYNC
const int horizontalBytes = 60; // 480 pixels wide
const int verticalPixels = 480; // 480 pixels high
// Pixel time = ((1/60) / 525 * 1e9) / 800 = 39.68 nS
// frequency = 1 / (((1/60) / 525 * 1e6) / 800) = 25.2 MHz
// However in practice, it we can only pump out pixels at 375 nS each because it
// takes 6 clock cycles to read one in from RAM and send it out the port.
const int verticalLines = verticalPixels / 16;
const int horizontalPixels = horizontalBytes * 8;
const byte verticalBackPorchLines = 35; // includes sync pulse?
const int verticalFrontPorchLines = 525 - verticalBackPorchLines;
volatile int vLine;
volatile int messageLine;
volatile byte backPorchLinesToGo;
#define nop asm volatile ("nop\n\t")
// bitmap - gets sent to PORTD
// For D4/D5/D6 bits need to be shifted left 4 bits
// ie. 00BGR0000
char message [verticalLines] [horizontalBytes];
// ISR: Vsync pulse
ISR (TIMER1_OVF_vect) {
vLine = 0;
messageLine = 0;
backPorchLinesToGo = verticalBackPorchLines;
} // end of TIMER1_OVF_vect
// ISR: Hsync pulse ... this interrupt merely wakes us up
ISR (TIMER2_OVF_vect) {
} // end of TIMER2_OVF_vect
void setup() {
// initial bitmap ... change to suit
for (int y = 0; y < verticalLines; y++)
for (int x = 0; x < horizontalBytes; x++)
message [y] [x] = (x + y) << 4;
// disable Timer 0
TIMSK0 = 0; // no interrupts on Timer 0
OCR0A = 0; // and turn it off
OCR0B = 0;
// Timer 1 - vertical sync pulses
pinMode (vSyncPin, OUTPUT);
TCCR1A = 0;
TCCR1B = 0;
TCCR1A |= (bit (WGM10) | bit (WGM11)) | bit (COM1B1);
TCCR1B |= (bit (WGM12) | bit (WGM13)) | 0B00000101;
OCR1A = 259; // 16666 / 64 uS = 260 (less one)
OCR1B = 0; // 64 / 64 uS = 1 (less one)
TIFR1 = bit (TOV1); // clear overflow flag
TIMSK1 = bit (TOIE1); // interrupt on overflow on timer 1
// Timer 2 - horizontal sync pulses
pinMode (hSyncPin, OUTPUT);
TCCR2A = 0;
TCCR2B = 0;
TCCR2A |= (bit (WGM20) | bit (WGM21)) | bit (COM2B1);
TCCR2B |= (bit (WGM22)) | 0B00000010;
OCR2A = 63; // 32 / 0.5 uS = 64 (less one)
OCR2B = 7; // 4 / 0.5 uS = 8 (less one)
TIFR2 = bit (TOV2); // clear overflow flag
TIMSK2 = bit (TOIE2); // interrupt on overflow on timer 2
// prepare to sleep between horizontal sync pulses
set_sleep_mode (SLEEP_MODE_IDLE);
// pins for outputting the colour information
pinMode (redPin, OUTPUT);
pinMode (greenPin, OUTPUT);
pinMode (bluePin, OUTPUT);
} // end of setup
// draw a single scan line
void doOneScanLine () {
// after vsync we do the back porch
if (backPorchLinesToGo)
{
backPorchLinesToGo--;
return;
} // end still doing back porch
// if all lines done, do the front porch
if (vLine >= verticalPixels)
return;
// pre-load pointer for speed
register char * messagePtr = & (message [messageLine] [0] );
delayMicroseconds (1);
// how many pixels to send
register byte i = horizontalBytes;
// blit pixel data to screen
while (i--)
PORTD = * messagePtr++;
// stretch final pixel
nop; nop; nop;
PORTD = 0; // back to black
// finished this line
vLine++;
// every 16 pixels it is time to move to a new line in our text
if ((vLine & 0xF) == 0)
messageLine++;
} // end of doOneScanLine
void loop() {
// sleep to ensure we start up in a predictable way
sleep_mode ();
doOneScanLine ();
} // end of loop
I can see the problem at first glance, black lines are generated between each line that I draw (each color pixel has a height of 16 lines, which 8 are of the corresponding color and the other 8 are black, the image is 60 x 30 pixels and only 60 x 15 is displayed) which also causes only half the image to be displayed.
With a vague thought I thought that I would solve this by changing this code:
// every 16 pixels it is time to move to a new line in our text
if ((vLine & 0xF) == 0)
messageLine++;
Change 0x0F to 0x07, this divides the height of the pixels by 2, but the black lines are still there (the good thing about this is that the whole image is displayed).
I have some suspicions with "sleep_mode()" which is executed inside the loop before each time a line is drawn. I tried to remove it, but this creates a mess on the monitor (I think if I had done it on an old monitor I would have broken it). I don't know much about the "avr/sleep.h" library, the best thing would be to see how long this sleep_mode takes to then execute the "doOneScanLine()" function, but I don't have an oscilloscope.
There are many factors that can produce this, so I accept any suggestion or if you know what produces this, I would be very grateful if you told me.
Thanks!!!