You Are Here: Home » AVR ATmega Projects » Video - Camera - Imaging Projects » AVR VideoBlaster: How about NTSC color video on a single chip with just 2 resistors?

AVR VideoBlaster: How about NTSC color video on a single chip with just 2 resistors?




Story

More VideoBlasting

So why didn’t the TVout library reach any higher resolution than 160×100?

The answer is simple.
They did not use any hardware onchip to push the pixels out.

If you use the SPI to push the pixel you will gain an immediate 1:1 in pixelspeed
The SPI push out data at the CPU clock / 2.
That is the maximum speed that the chip is capable of.

On a 16MHz chip, that is an impressive 8mbps.
That is really fast, 8000000/sec databits!AVR VideoBlaster How about NTSC color video on a single chip with just 2 resistors

On a TV you have around 64uS time to draw a single line (around taking NTSC/PAL in account)

That meens you have hardware to push out 512pixels in that interval.

The no good SPI

So why not use SPI for the pixel pushing?

There is one flaw in the ATmega SPI hardware.
It shifts out 8pixels and delays for one pixel before working on our next 8pixels.

Why?
It is an SPI interface and has to conform to that protocol and that states that there should be a high 9bit after a transfer.

Not good for us. Not at all
We need 8bits, one byte, back to back drawing our videoline.

There is a solution to this.
The USART.

Yes I know its the serial communication via USB to your computer.
But it it has a huge advantage.

It can run at the same speed as the SPI interface but no 9th bit.
That’s right. Unlike the SPI port it doesn’t do the extra bit.

This is exactly what we need.

The VideoBlaster resistor mixer

The resistor mixer adds the sync and pixeldata to a composite videosignal.
If looking at the NTSC video signal, the 1K resistor is adding 0.3v level sync pulses and the 470ohm resistor is the 0.7v pixel or video data.
When summing it becomes the 1vp-p CBVS signal (Composite Blanking and Video Sync) or composite video.
It is the same circuit that all TVout sketches use but we connect the video resistor to the TX pin instead of a digitalpin.
It works on all ATmegas and Arduinoboards even if its connected to a computer.
You loose the serial debugging but not the serial uploading.
But you gain a videooutput and that can display your serial text instead of sending it to the computer.
And the pixelshifter runs at Fcpu/2 and that meens 512pixels/line at Fcpu 16MHz.

To be able to generate the NTSC Chromasignal Fcpu needs to be 14.318MHz

If not exactly that it will still show the image but it will be in B/W.

The Color VideoBlaster Code

the_color_videoblaster_code.
The Color VideoBlaster Code
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#define PS2_KC_BKSP    0x80
#define PS2_KC_UP      0x81
#define PS2_KC_DOWN    0x82
#define PS2_KC_LEFT    0x83
#define PS2_KC_RIGHT   0x84
#define PS2_KC_PGDN    0x85
#define PS2_KC_PGUP    0x86
#define PS2_KC_END     0x87
#define PS2_KC_HOME    0x88
#define PS2_KC_INS     0x89
#define PS2_KC_DEL     0x8A
#define PS2_KC_ESC     0x8B
#define PS2_KC_CLON    0x8C // caps_lock on
#define PS2_KC_CLOFF   0x8D // caps_lock off

const byte MSPIM_SCK = 0;

byte PROGMEM charROM [1024] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E,
0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E,
0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00,
0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00,
0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x7C, 0x38, 0x7C,
0x10, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C,
0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00,
0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF,
0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00,
0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF,
0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78,
0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18,
0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0,
0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0,
0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99,
0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00,
0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00,
0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18,
0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00,
0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0xCC, 0x78,
0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00,
0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF,
0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00,
0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00,
0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00,
0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00,
0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00,
0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00,
0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00,
0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00,
0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00,
0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00,
0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00,
0x7C, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0x7C, 0x00,
0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00,
0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00,
0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00,
0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00,
0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00,
0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00,
0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00,
0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00,
0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00,
0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00,
0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00,
0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00,
0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x78, 0x00,
0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00,
0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00,
0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00,
0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00,
0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00,
0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00,
0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3E, 0x00,
0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00,
0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00,
0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00,
0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00,
0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00,
0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00,
0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00,
0x78, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x1C, 0x00,
0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00,
0x78, 0xCC, 0xE0, 0x70, 0x1C, 0xCC, 0x78, 0x00,
0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00,
0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00,
0xC6, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x00,
0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00,
0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00,
0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00,
0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00,
0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00,
0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00,
0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xF0, 0x00,
0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8,
0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00,
0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78,
0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00,
0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xC6, 0x00,
0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00,
0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00,
0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0,
0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E,
0x00, 0x00, 0xDC, 0x76, 0x66, 0x60, 0xF0, 0x00,
0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x00,
0x10, 0x30, 0x7C, 0x30, 0x30, 0x34, 0x18, 0x00,
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00,
0x00, 0x00, 0xC6, 0xD6, 0xFE, 0xFE, 0x6C, 0x00,
0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00,
0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8,
0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00,
0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00,
0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00,
0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00,
};


unsigned int scanline=0;
unsigned int videoptr=0;
volatile byte VBE=0;
byte xpos=0;
byte ypos=0;
byte txtback=0;
byte txtfore=255;

boolean ps2Keyboard_shift;     // indicates shift key is pressed
boolean ps2Keyboard_ctrl;      // indicates the ctrl key is pressed
boolean ps2Keyboard_alt;       // indicates the alt key is pressed
boolean ps2Keyboard_extend;    // remembers a keyboard extended char received
boolean ps2Keyboard_release;   // distinguishes key presses from releases
boolean ps2Keyboard_caps_lock; // remembers shift lock has been pressed

char videomem[8000];

void setup() {
  
  //Setup for NTSC with 14.318MHz Fcpu clock
  UBRR0 = 0; // must be zero before enabling the transmitter
  UCSR0A = _BV (TXC0);  // any old transmit now complete
  pinMode (MSPIM_SCK, OUTPUT);   // set XCK pin as output to enable master mode
  UCSR0C = _BV (UMSEL00) | _BV (UMSEL01);  // Master SPI mode
  UCSR0B = _BV (TXEN0);  // transmit enable
  // must be done last, see page 206
  UBRR0 = 0;  // 7.16MHz pixel clock
  pinMode(15, OUTPUT); //Set PD7 as output for Sync
  pinMode(8, OUTPUT); //Set PD0 as output for video
  cli();
  //set timer0 interrupt at 15699Hz
  TCCR0A = 0;// set entire TCCR0A register to 0
  TCCR0B = 0;// same for TCCR0B
  TCNT0  = 0;//initialize counter value to 0
  // set compare match register for 15699hz increments
  OCR0A = 113;// = (16*10^6) / (15699*8) - 1 (must be <256)
   
  // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  // Set CS01 and CS00 bits for 9 prescaler
  TCCR0B |= (1 << CS01) | (0 << CS00);   
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);
  set_sleep_mode (SLEEP_MODE_IDLE);
  sei();
  
  //------------ PS/2 Keyboard setup ----------------------------
  
  // Enable receiver
  UCSR1B = 16 | 8;
  /* Set frame format: 1data, 1parity, 1stop bit */
  UCSR1C = 64 | 6;
  
  ps2Keyboard_shift         = false;
  ps2Keyboard_ctrl          = false;
  ps2Keyboard_alt           = false;
  ps2Keyboard_extend        = false;
  ps2Keyboard_release       = false;
  ps2Keyboard_caps_lock     = false;
  
  //----------------------------------------------------------------------------------------------
  
  for (int x=0; x <8000; x++){
     videomem[x]=B01010101;
  }
 
  


}

void TVdelay(unsigned int millisec) {
  unsigned long cnt=millisec*100L;
  for (unsigned long x=0; x < cnt; x++){  
  while (VBE==1) sleep_cpu();
  }
}

  




void loop() {
 TVdelay(100); //Dont use delay() to make delays, but TVdelay(millis) to do delays, there is no delay function.

 printstring("\x80\x87\fBambino BIOS v1.01\n\n\n"); //clear the screen with BGcolor black and FGcolor white.
 printstring("VideoBlaster v1.05\n");
  
  while(-1) {
    while (VBE==1) sleep_cpu();
    
    //If VBE=1 the CPU have to sleep for the video to be smooth.
    
    //put anything you like in this loop but make sure it returns before the next frame is painted.

    // Use printstring(string) to write an entier string to chrout.
    // Use chrout(char ascii) to write a singel char to the terminal screen. chr x80 - x87 sets BG and FG attributes.
    // Use chrin() to read a singel char from the keyboard.
    // use pset(x,y) to set a single dot on the 320x200 screen.
    // Use point(x,y,c) to set a color dot on the 320x200 screen (c is color 0-3).
    // All the other functions are used by the Bambino BIOS and wont remain the same in the future.
    // Dont touch the variables scanline and videoptr, they are not volatile on purpose because of speed reasons.

    
  }

}  
  
ISR(TIMER0_COMPA_vect){//timer0 interrupt
  asm("nop\n");
  byte p=40;
  UCSR0C = 192;
  UCSR0B = _BV(TXEN0);
  PORTD = 0;
      
  if ((scanline>2)&&(scanline<40)||(scanline>239)) {
    UCSR0B = 0;
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");  // Dont touch any of these NOPs
    asm("nop\n");  // They are all here for timing.
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");   
    PORTD =128;
    VBE=0;    
  }

 if (scanline<3) {
    UCSR0B = 0;
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");   
    PORTD =0;

    videoptr=0;   
  }  
  
  if ((scanline>39)&&(scanline<240)) {

    UDR0 = 0x00; //Load first byte
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    asm("nop\n");
    //asm("nop\n");   

    PORTD =128;
    VBE=1;
    //asm("nop\n");
    //asm("nop\n");


     //Color burst
     
     UDR0 = B00001010;

     UDR0 = B10101010;
     while ((UCSR0A & _BV (UDRE0)) == 0)
      {}

     UDR0 = B10101000;
     while ((UCSR0A & _BV (UDRE0)) == 0)
      {}
     UDR0 = B00000000;
     while ((UCSR0A & _BV (UDRE0)) == 0)
      {}
      

     
     //Back porch 
     UCSR0C = 193; //This is the phasechange to the colorburst. If you put 192 here instead, you will get blue/green as colors.
     UDR0 = B00000000;
     while ((UCSR0A & _BV (UDRE0)) == 0)
      {}
     UDR0 = B00000000;
     
     while ((UCSR0A & _BV (UDRE0)) == 0)
      {}
     
     
     
      
     //UDR0 = B00000000;
  
    
     //UCSR0C = 193;
     UDR0 = 0;

   

    
     
    
    while (p--) {
    while ((UCSR0A & _BV (UDRE0)) == 0)
     {}  
    UDR0 = videomem[videoptr++];
    }

  }
  UCSR0B = 0;
  scanline++;

  if (scanline>261) scanline=0;
   
}

void pset(unsigned int x,unsigned y) {
  unsigned int pixelbyte=videoptr+(y*40)+(x>>3);
  videomem[pixelbyte]=videomem[pixelbyte] | (128>>(x&7));
}

void point(unsigned int x,unsigned y,byte color) {
  x=x&510;
  color=((color<<6)&192)>>(x&7);
  unsigned int pixelbyte=videoptr+(y*40)+(x>>3);
  byte mask=255-(192>>(x&7));
  videomem[pixelbyte]=videomem[pixelbyte] & mask | color;
}

void screenclr(byte bgcolor) {
  bgcolor=bgcolor&3;
  bgcolor=bgcolor | (bgcolor<<2);
  bgcolor=bgcolor | (bgcolor<<4);
  for (int x=0; x <8000; x++){ 
   videomem[x]=bgcolor;
  }
  xpos=0;
  ypos=0;
}

void drawchar(byte x,byte y,byte ascii) {
 if ((x<40)&&(y<25)) { 
  for (byte i=0; i < 8; i++) {
   byte mask=(255-pgm_read_byte(&charROM[(ascii*8)+i]))&txtback;
   videomem[(y*320)+x+(i*40)]=(pgm_read_byte(&charROM[(ascii*8)+i])&txtfore)|mask;
  }  
 } 
}

void chrout(char ascii) {
  switch (ascii) {
    case 13:
      xpos=0;
      ypos++;
      if (ypos>24) {
       scrollscr(); 
       xpos=0;
       ypos=24;
      }
      break;
    case 10:
      xpos=0;
      ypos++;
      if (ypos>24) {
       scrollscr(); 
       xpos=0;
       ypos=24;
      }
      break;
    case 12:
      screenclr(txtback&3);
      xpos=0;
      ypos=0;
      break;      
    case 0x80:
      settxtBGcolor(0);
      break;
    case 0x81:
      settxtBGcolor(1);
      break;
    case 0x82:
      settxtBGcolor(2);
      break;
    case 0x83:
      settxtBGcolor(3);
      break;
    case 0x84:
      settxtFGcolor(0);
      break;
    case 0x85:
      settxtFGcolor(1);
      break;
    case 0x86:
      settxtFGcolor(2);
      break;
    case 0x87:
      settxtFGcolor(3);
      break;      
    default: 
    drawchar(xpos,ypos,ascii);
    xpos++;
    if (xpos>39) {
     xpos=0;
     ypos++;
     if (ypos>24) {
      scrollscr(); 
      xpos=0;
      ypos=24;
     }
    }
   }  
}

void scrollscr() {
  for (unsigned int i=320; i < 8000; i++) {
    videomem[i-320]=videomem[i];
  }
  for (unsigned int i=7680; i < 8000; i++) {
    videomem[i]=0;
  }  
}

void settxtBGcolor(byte color) {
  color=color&3;
  color=color | (color<<2);
  color=color | (color<<4);
  txtback=color;
}

void settxtFGcolor(byte color) {
  color=color&3;
  color=color | (color<<2);
  color=color | (color<<4);
  txtfore=color;
}

void printstring(char string[]) {
 byte i=0;
 while(string[i]) {
   chrout(string[i++]);
 }  
 
}

char chrin() {
 byte result=0;
 byte realkey=0; 
 if (UCSR1A&128) {
  result=UDR1;

    switch (result) {
    case 0xF0: { // key release char
      ps2Keyboard_release = true;
      ps2Keyboard_extend = false;
      result=0;
      break;
    }
    case 0xFA: { // command acknowlegde byte
      
      break;
    }
    case 0xE0: { // extended char set
      ps2Keyboard_extend = true;
      break;
    }
    case 0x12:   // left shift
    case 0x59: { // right shift
      ps2Keyboard_shift = ps2Keyboard_release? false : true;
      ps2Keyboard_release = false;
      result=0;
      break;
    }
    case 0x11: { // alt key (right alt is extended 0x11)
      ps2Keyboard_alt = ps2Keyboard_release? false : true;
      ps2Keyboard_release = false;
      result=0;
      break;
    }
    case 0x14: { // ctrl key (right ctrl is extended 0x14)
      ps2Keyboard_ctrl = ps2Keyboard_release? false : true;
      ps2Keyboard_release = false;
      break;
    }
    case 0x58: { // caps lock key
      if (!ps2Keyboard_release) {
	ps2Keyboard_caps_lock = ps2Keyboard_caps_lock? false : true;
      }
      else {
	ps2Keyboard_release = false;
      }
      result=0;
      break;
    }
    default: { // a real key
      if (ps2Keyboard_release) { // although ignore if its just released
        ps2Keyboard_release = false;
        result=0;
      }
      else { // real keys go into CharBuffer
        realkey=result;
      }
    }
    }

 switch (result) {
 
  case 0x1C: result = 'a'; break;
  case 0x32: result = 'b'; break;
  case 0x21: result = 'c'; break;
  case 0x23: result = 'd'; break;
  case 0x24: result = 'e'; break;
  case 0x2B: result = 'f'; break;
  case 0x34: result = 'g'; break;
  case 0x33: result = 'h'; break;
  case 0x43: result = 'i'; break;
  case 0x3B: result = 'j'; break;
  case 0x42: result = 'k'; break;
  case 0x4B: result = 'l'; break;
  case 0x3A: result = 'm'; break;
  case 0x31: result = 'n'; break;
  case 0x44: result = 'o'; break;
  case 0x4D: result = 'p'; break;
  case 0x15: result = 'q'; break;
  case 0x2D: result = 'r'; break;
  case 0x1B: result = 's'; break;
  case 0x2C: result = 't'; break;
  case 0x3C: result = 'u'; break;
  case 0x2A: result = 'v'; break;
  case 0x1D: result = 'w'; break;
  case 0x22: result = 'x'; break;
  case 0x35: result = 'y'; break;
  case 0x1A: result = 'z'; break;

    // note that caps lock only used on a-z
  case 0x41: result = ps2Keyboard_shift? ';' : ','; break;
  case 0x49: result = ps2Keyboard_shift? ':' : '.'; break;
  case 0x4A: result = ps2Keyboard_shift? '?' : '/'; break;
  case 0x54: result = ps2Keyboard_shift? '{' : '['; break;
  case 0x5B: result = ps2Keyboard_shift? '}' : ']'; break;
  case 0x4E: result = ps2Keyboard_shift? '_' : '-'; break;
  case 0x55: result = ps2Keyboard_shift? '+' : '='; break;
  case 0x29: result = ' '; break;

  case 0x45: result = ps2Keyboard_shift? '=' : '0'; break;
  case 0x16: result = ps2Keyboard_shift? '!' : '1'; break;
  case 0x1E: result = ps2Keyboard_shift? 0x22 : '2'; break;
  case 0x26: result = ps2Keyboard_shift? '#' : '3'; break;
  case 0x25: result = ps2Keyboard_shift? '$' : '4'; break;
  case 0x2E: result = ps2Keyboard_shift? '%' : '5'; break;
  case 0x36: result = ps2Keyboard_shift? '&' : '6'; break;
  case 0x3D: result = ps2Keyboard_shift? '/' : '7'; break;
  case 0x3E: result = ps2Keyboard_shift? '(' : '8'; break;
  case 0x46: result = ps2Keyboard_shift? ')' : '9'; break;

  case 0x0D: result = '\t'; break;
  case 0x5A: result = 13; break;
  case 0x66: result = PS2_KC_BKSP;  break;
  case 0x69: result = ps2Keyboard_extend? PS2_KC_END   : '1'; break;
  case 0x6B: result = ps2Keyboard_extend? PS2_KC_LEFT  : '4'; break;
  case 0x6C: result = ps2Keyboard_extend? PS2_KC_HOME  : '7'; break;
  case 0x70: result = ps2Keyboard_extend? PS2_KC_INS   : '0'; break;
  case 0x71: result = ps2Keyboard_extend? PS2_KC_DEL   : '.'; break;
  case 0x72: result = ps2Keyboard_extend? PS2_KC_DOWN  : '2'; break;
  case 0x73: result = '5'; break;
  case 0x74: result = ps2Keyboard_extend? PS2_KC_RIGHT : '6'; break;
  case 0x75: result = ps2Keyboard_extend? PS2_KC_UP    : '8'; break;
  case 0x76: result = PS2_KC_ESC; break;
  case 0x79: result = '+'; break;
  case 0x7A: result = ps2Keyboard_extend? PS2_KC_PGDN  : '3'; break;
  case 0x7B: result = '-'; break;
  case 0x7C: result = '*'; break;
  case 0x7D: result = ps2Keyboard_extend? PS2_KC_PGUP  : '9'; break;

  } // end switch(result)
 }
   if (((result>='a') && (result<='z')) &&
      ((ps2Keyboard_shift && !ps2Keyboard_caps_lock) ||
       (!ps2Keyboard_shift && ps2Keyboard_caps_lock))) {
    result = result + ('A'-'a');
   }
 
  return result;
}

AVR VideoBlaster

I’ve been on the net for a while and so have all of you.

Ever since I started with the Arduino and after that, the ATmega chip, I was always fascinated of doing video on the chip.

The chip is fully capable of doing video with its 16MIPS processing power. But B/W video isn’t very funny. At least not to me.

TVout is one of the things that many people come across.

That gives you B/W composite video graphics on a TV screen.
In a 160×100 maximum resolution.

That is actually a very good resolution for a TV display.
But still in Black&White

What I did was to take the TVout 2 steps further.

I made the resolution 320×200 and in color.

Yes, I broke the imaginary border.
You read it right the first time.Resistor Mixer

That is 40×25 characters in text and a dazzling 320×200 in graphics.
And its all in color.

(Yes that is 40×25 textmode on any Uno/Nano but only in B/W)

High resolution color on a single ATmega chip.

I’ll show you the real circuit and the code.

The VideoBlaster Resistor Mixer

For more detail: AVR VideoBlaster: How about NTSC color video on a single chip with just 2 resistors?

Leave a Comment

You must be logged in to post a comment.

Scroll to top