Black lines in VGA

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!!!

Is there any working code with the tutorial that you can test?

Those images are generated with this code, I don’t know if I misunderstood the question, because the code was already in the post. Or I don’t know if you mean that if I have that modified code that is close to the desired result? No, that code is the only thing I have, but for some reason it generates black lines.

#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

Now I am confused. Did you post modified, or unmodified code in reply #2? Also did you try running unmodified code and what was the result, if so?

Sorry, I just realized what you meant, the second code does not generate black lines, but I can’t find out why not here and in the first code yes. Maybe, I think, since the second code is a small animation, the times are offset? I have no idea, it is almost impossible for me to find out without a scilloscope. That first code is not modified, it is only somewhat simplified, but the result is exactly the same.

Second code:

#include <TimerHelpers.h>
#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 = 50;  // 480 pixels wide
const int verticalPixels = 480;  // 480 pixels high

// Timer 1 - Vertical sync

// output    OC1B   pin 16  (D10) <------- VSYNC

//   Period: 16.64 mS (60 Hz)
//      1/60 * 1e6 = 16666.66 uS
//   Pulse for 64 uS  (2 x HSync width of 32 uS)
//    Sync pulse: 2 lines
//    Back porch: 33 lines
//    Active video: 480 lines
//    Front porch: 10 lines
//       Total: 525 lines

// Timer 2 - Horizontal sync

// output    OC2B   pin 5  (D3)   <------- HSYNC

//   Period: 32 uS (31.25 kHz)
//      (1/60) / 525 * 1e6 = 31.74 uS
//   Pulse for 4 uS (96 times 39.68 nS)
//    Sync pulse: 96 pixels
//    Back porch: 48 pixels
//    Active video: 640 pixels
//    Front porch: 16 pixels
//       Total: 800 pixels

// 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 int backPorchLinesToGo;
volatile byte newFrame;

#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;
  newFrame = true;
  } // end of TIMER1_OVF_vect
  
// ISR: Hsync pulse ... this interrupt merely wakes us up
ISR (TIMER2_OVF_vect)
  {
  backPorchLinesToGo--;    
  } // 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] = (7) << 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); 
  Timer1::setMode (15, Timer1::PRESCALE_1024, Timer1::CLEAR_B_ON_COMPARE);
  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); 
  Timer2::setMode (7, Timer2::PRESCALE_8, Timer2::CLEAR_B_ON_COMPARE);
  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
boolean doOneScanLine ()
  {
    
  // after vsync we do the back porch
  if (backPorchLinesToGo > 0)
    {
    backPorchLinesToGo--;
    return false;   
    }  // end still doing back porch
    
  // if all lines done, do the front porch
  if (vLine == verticalPixels)
    return newFrame;
    
  // 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++;
    
  return false;
  }  // end of doOneScanLine

float radians = 0;
const float pi = 3.1415926;
const float radiansIncrement = (pi / 2.0) / (horizontalBytes / 2);
byte x;
boolean Up = true;
byte colour = 0;
boolean Calc = true;

void advanceLine ()
  {
  if (Calc)
    {
    x = sin (radians) * horizontalBytes;
    if (Up)
      {
      radians += radiansIncrement;
      if (radians >= pi / 2)
        Up = false;
      }
    else
      {
      radians -= radiansIncrement;
      if (radians <= 0)
        {
        Up = true;
        radians = 0;
        colour++;
        }
      }
    Calc = false;
    }
  else
    {
    memmove (& message [0] [0], & message [1] [0], sizeof message - horizontalBytes);  
    memset (&message [verticalLines - 1] [0], (colour + 1) << 4, horizontalBytes);
    memset (&message [verticalLines - 1] [0], colour << 4, x);
    Calc = true;
    }

  newFrame = false;
  }
  
void loop() 
  {
  // loop to avoid overhead of function call
  while (true)
    {
    // sleep to ensure we start up in a predictable way
    sleep_mode ();
    if (doOneScanLine ())
      advanceLine ();
    }  // end of while
 }  // end of loop

That first code is not modified, it is only somewhat simplified,

What does that mean?

By modify I mean to change or add code, and by simplify I mean to delete comments and delete the #include TimersHelp (a very small and easy to understand library, and what I did was copy and paste the code snippets that are used to my code, so as not to have to download the library).

hiperdoo: By modify I mean to change or add code, and by simplify I mean to delete comments and delete the #include TimersHelp (a very small and easy to understand library, and what I did was copy and paste the code snippets that are used to my code, so as not to have to download the library).

What was the purpose of that? How is it helpful to delete comments? Or to cut and paste snippets?

I only did that so that the code post is less long and so that you could run it without having to download the library, it didn't cost me any time to do that. But that does not affect the operation of the code at all.

The main issue is that I have no idea where those black lines come from.

hiperdoo:
I only did that so that the code post is less long and so that you could run it without having to download the library, it didn’t cost me any time to do that. But that does not affect the operation of the code at all.

The main issue is that I have no idea where those black lines come from.

Don’t the black lines affect the operation of the code? Just saying… some people might ask.

Technically these black lines besides being ugly if they affect the operation of the code, in this case, vertically there are 480 lines, each pixel has a height of 16 lines, and there are 30 pixels that make up a column (30 x 16 = 480). But the black lines affect the size of the pixels, making them 32 lines high, which also causes half the image to not be displayed (30 x 32 = 960, wrong). And with the modified code (which is where I change the 0x0F to 0x07) the pixels now would not have a height of 16 lines but 8 lines, this would only generate an image of 240 lines, but with the black lines it is compensated. The problem is that I don't know where those black lines come from and I need to get rid of them.

Please explain what changes you want to happen in the display image, and explain what changes you made in the code that you hope will do that. Give lots of code examples and detail (in other words don’t leave it for us to try to figure out from comparing two complex sketches).

Also confusing, is the reference to the “second code” in reply #4. You say it works, so why not use it?

Well, I explained everything in the first message, in short I want to know what produces the black lines that are generated after each line that I draw to fix it (from the first code). The second code is in the 3rd message that I send, that code I send it because in this code the black lines are not generated (as something to be guided by why the black lines are generated).

hiperdoo: Well, I explained everything in the first message, in short I want to know what produces the black lines that are generated after each line that I draw to fix it (from the first code).

You see, that's just leaving the burden on us to dig down and try to figure out what changes you made. Because, you didn't really explain much of that in the first post. Just a snippet. That's really not fair. So someone else will have to help you solve this.

I’m explaining what changes I made, I made ONE change that allows me to see all the pixels reducing the number of lines drawn by 2 because the black lines take half of the entire drawing (speaking of vertical lines, horizontal lines are fine).

I do not know why you say that it is unfair that I am not saying all the changes that I made, I only made one, I consider that I explain it well by putting the code and saying which part is the one that changes (I mean, only change the value of 0x0F for 0x07). I have to admit that I did try changing other things but it only destroyed the image, so it is totally unnecessary to write all the things I did today trying to eliminate the black lines.

Summing up everything I said in this post:

#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

This is the code I am working on, WITHOUT CHANGING anything it generate black lines, which causes half of the image not to be displayed. My request: that someone explain to me why these black lines are generated, because my attempts have not been close to solving it, the only attempt worth showing is to change the value 0x0F to 0x07, because it shows that the image can be displayed completely if I divide the pixels by 2, since the lines take that place.

I have no more information that can help me find out why these black lines are generated. My opinion: I think that with an oscilloscope you can discover what is really happening, but I don’t have one because of its high cost.

I don't know if you are confused by the code that I sent in my second message, I sent that message because I had not understood the first message that you had sent well, but that code is exactly the same. And the code of the third message that I send is a code that does not generate the black lines, in case you want to compare it with the first code to see what the problem is.

I really need help, I have no idea what produces these horrible black lines.