Go Down

Topic: Arduino + MAX7456 OSD (Read 41601 times) previous topic - next topic

dfraser

Being able to overlay text on video has a number of uses.  I needed this ability while building a couple of different SMPTE timecode readers.  While there were a selection of inexpensive OSD chips, for use in VHS and TV sets, that market seems to dried up lately.  Luckily Maxim has just come out with new OSD chip.

This is a  platform for experimenting with the MAX7456 OSD chip.  It is a surface mount chip and is more easily used if attached to breakout board.

The shield wiring is pretty straight forward, +5, ground some .1 bypass caps, however you can get it all to fit the best.  Please see the schematic.

Schematic



Parts you will need:
5  .1uf caps
2  75 ohm resistors
1  10K ohm resistor
1  100uf cap
1  22uf cap
1  27Mhx crystal
1  SparkFun 28 pin SSOP to DIP Adapter
1  NKC proto shield
1  28 DIP socket

The OSD accepts commands from the Arduino over the SPI bus. I did comment the code, more then I normally do anyway.  So I hope is easy to follow.  Once you get the board setup you can issue serial commands from the sketch serial monitor, or HyperTerm.  You can also create a text file and download it into the overlay.

Part 1
Code: [Select]

#include <EEPROM.h> //Needed to access eeprom read/write functions

#define DATAOUT 11//MOSI
#define DATAIN  12//MISO
#define SPICLOCK  13//sck
#define MAX7456SELECT 10//ss
#define VSYNC 2// INT0

//MAX7456 opcodes
#define DMM_reg   0x04
#define DMAH_reg  0x05
#define DMAL_reg  0x06
#define DMDI_reg  0x07
#define VM0_reg   0x00
#define VM1_reg   0x01

//MAX7456 commands
#define CLEAR_display 0x04
#define CLEAR_display_vert 0x06
#define END_string 0xff
// with NTSC
#define ENABLE_display 0x08
#define ENABLE_display_vert 0x0c
#define MAX7456_reset 0x02
#define DISABLE_display 0x00

// with PAL
// all VM0_reg commands need bit 6 set
//#define ENABLE_display 0x48
//#define ENABLE_display_vert 0x4c
//#define MAX7456_reset 0x42
//#define DISABLE_display 0x40

#define WHITE_level_80 0x03
#define WHITE_level_90 0x02
#define WHITE_level_100 0x01
#define WHITE_level_120 0x00

// with NTSC
#define MAX_screen_size 390
#define MAX_screen_rows 13

// with PAL
//#define MAX_screen_size 480
//#define MAX_screen_rows 16

#define EEPROM_address_hi 510
#define EEPROM_address_low 511
#define EEPROM_sig_hi 'e'
#define EEPROM_sig_low 's'

volatile byte screen_buffer[MAX_screen_size];

volatile byte writeOK;
volatile byte valid_string;
volatile byte save_screen;
volatile int  incomingByte;
volatile int  count;

//
//##############################
//# 30 characters per line     #
//# 13 lines per screen        #
//# start'[' end ']' 390 chars #
//# add '~' if front of '[' to #
//# force write to eeprom      #
//# '~[]' to zero out eeprom   #
//##############################
//
//[
//
//
//       THIS IS A TEST
//
//    Arduino / MAX7456-OSD
//
//  ABCDEFGHIJKLMNOPQRSTUVWXYZ
//         0123456789
//]
//

//////////////////////////////////////////////////////////////
void setup()
{
 byte spi_junk, eeprom_junk;
 int x;
 Serial.begin(9600);
 Serial.flush();

 pinMode(MAX7456SELECT,OUTPUT);
 digitalWrite(MAX7456SELECT,HIGH); //disable device

 pinMode(DATAOUT, OUTPUT);
 pinMode(DATAIN, INPUT);
 pinMode(SPICLOCK,OUTPUT);
 pinMode(VSYNC, INPUT);

 // SPCR = 01010000
 //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
 //sample on leading edge of clk,system clock/4 rate (4 meg)
 SPCR = (1<<SPE)|(1<<MSTR);
 spi_junk=SPSR;
 spi_junk=SPDR;
 delay(250);

 // force soft reset on Max7456
 digitalWrite(MAX7456SELECT,LOW);
 spi_transfer(VM0_reg);
 spi_transfer(MAX7456_reset);
 digitalWrite(MAX7456SELECT,HIGH);
 delay(500);

 // set all rows to same charactor white level, 90%
 digitalWrite(MAX7456SELECT,LOW);
 for (x = 0; x < MAX_screen_rows; x++)
 {
   spi_transfer(x + 0x10);
   spi_transfer(WHITE_level_90);
 }

 // make sure the Max7456 is enabled
 spi_transfer(VM0_reg);
 spi_transfer(ENABLE_display);
 digitalWrite(MAX7456SELECT,HIGH);


 // clear the array
 for (x = 0; x < MAX_screen_size; x++)
 {
   screen_buffer[x] = 0x00;
 }

 writeOK = false;
 valid_string = false;
 save_screen = false;
 incomingByte = 0;
 count = 0;

// check to see if we have a default screen stored in eeprom
 eeprom_junk = EEPROM.read(EEPROM_address_hi);
 if (eeprom_junk == EEPROM_sig_hi)
 {
   eeprom_junk = EEPROM.read(EEPROM_address_low);
   if (eeprom_junk == EEPROM_sig_low)
   {
     for (x = 0; x < MAX_screen_size; x++)
     {
       screen_buffer[x] = EEPROM.read(x);
     }
     writeOK = true;
     count = MAX_screen_size;
   }
 }

 Serial.println("Ready for text file download");
 Serial.println("");
 delay(100);  
}


Arduino with MAX7456 Shield



**
[this is a test] prints on line one upper left screen

**
[
              this is a test] prints on the third line center of the screen

**              
[] erases the video overlay

**
~[this is a test] writes the text to eeprom memory and displays it at boot time

**
~[] zeros out the eeprom

Output


dfraser

Here is Part 2 of the Ardunio + MAX7456 code

Code: [Select]

//////////////////////////////////////////////////////////////
void loop()
{
 int x, junk;
 
 if (Serial.available() > 0)
 {
   // read the incoming byte:
   incomingByte = Serial.read();

   if (incomingByte == 126) // this is the '~' for saving the screen
     save_screen = true;
     
   switch(incomingByte)
   {
     case 0x5b: // [ start of text string
       //Serial.println("start of text");
       count = 0;
       valid_string = true;
     break;
     case 0x0a: // line feed, ignore
       //Serial.println("ln");  
     break;
     case 0x0d: // carridge return, skip to start of next line
       //Serial.println("cr");  
       if (count < 30)
         count = 30;
       else
       {
         junk = (count % 30);
         count = (count + (30 - junk));
       }
     break;
     case 0x5d: // ] end of text
       //Serial.println("end of text");
       Serial.flush();
       valid_string = false;
       writeOK = true;
     break;
     default:
       if (valid_string)
       {
         //Serial.println("buffer chars");
         if (count == MAX_screen_size) // something is wrong, too many characters
           count = count -1;  // drop it back one to position 389
         screen_buffer[count] = convert_ascii(incomingByte);
         count++;
       }
     break;
   }
 }

 if (writeOK)
 {
   writeOK = false;
   write_new_screen();

   // have we received a '~' to save screen to eeprom
   if (save_screen)
   {
     save_screen = false;
     
     // write contents of screen buffer to eeprom
     if (count > 0)
     {
       for (x = 0; x < count; x++)
       {
         EEPROM.write(x, screen_buffer[x]);
       }
       EEPROM.write(EEPROM_address_hi, EEPROM_sig_hi);
       EEPROM.write(EEPROM_address_low, EEPROM_sig_low);
     }
     else // to erase all eeprom 'send ~[]'
     {
       for (x = 0; x < 512; x++)
       {
         EEPROM.write(x, 0);
       }
     }
   }

   // clear out screen_buffer
   for (x = 0; x < MAX_screen_size; x++)
   {
     screen_buffer[x] = 0x00;
   }
   count = 0;
 }  
}

//////////////////////////////////////////////////////////////
byte spi_transfer(volatile byte data)
{
 SPDR = data;                    // Start the transmission
 while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
 {
 };
 return SPDR;                    // return the received byte
}

//////////////////////////////////////////////////////////////
byte convert_ascii (int character)
{
// for some reason the MAX7456 does not follow ascii letter
// placement, so you have to have this odd lookup table
// todo... create an eeprom table that matches ascii
// and burn to the MAX7456

 byte lookup_char;

 if (character == 32)
   lookup_char = 0x00; // blank space
 else if (character == 48)
   lookup_char = 0x0a; // 0
 else if ((character > 48) && (character < 58))
   lookup_char = (character - 48); // 1-9
 else if ((character > 64) && (character < 90))
   lookup_char = (character - 54); // A-Z
 else if ((character > 96) && (character < 123))
   lookup_char = (character - 60); // a-z
 else if (character == 34)
   lookup_char = 0x48; // "
 else if (character == 39)
   lookup_char = 0x46; // '
 else if (character == 40)
   lookup_char = 0x3f; // (
 else if (character == 41)
   lookup_char = 0x40; // )
 else if (character == 44)
   lookup_char = 0x45; // ,
 else if (character == 45)
   lookup_char = 0x49; // -
 else if (character == 46)
   lookup_char = 0x41; // .
 else if (character == 47)
   lookup_char = 0x47; // /
 else if (character == 58)
   lookup_char = 0x44; // :
 else if (character == 59)
   lookup_char = 0x43; // ;
 else if (character == 60)
   lookup_char = 0x4a; // <
 else if (character == 62)
   lookup_char = 0x4b; // >
 else if (character == 63)
   lookup_char = 0x42; // ?
 else
   lookup_char = 0x00; // out of range, blank space

return (lookup_char);

}

//////////////////////////////////////////////////////////////
void write_new_screen()
{
 int x, local_count;
 byte char_address_hi, char_address_lo;
 byte screen_char;
 
 local_count = count;
 
 char_address_hi = 0;
 char_address_lo = 0;
//Serial.println("write_new_screen");  

 // clear the screen
 digitalWrite(MAX7456SELECT,LOW);
 spi_transfer(DMM_reg);
 spi_transfer(CLEAR_display);
 digitalWrite(MAX7456SELECT,HIGH);

 // disable display
 digitalWrite(MAX7456SELECT,LOW);
 spi_transfer(VM0_reg);
 spi_transfer(DISABLE_display);

 spi_transfer(DMM_reg); //dmm
 //spi_transfer(0x21); //16 bit trans background
 spi_transfer(0x01); //16 bit trans w/o background

 spi_transfer(DMAH_reg); // set start address high
 spi_transfer(char_address_hi);

 spi_transfer(DMAL_reg); // set start address low
 spi_transfer(char_address_lo);

 x = 0;
 while(local_count) // write out full screen
 {
   screen_char = screen_buffer[x];
   spi_transfer(DMDI_reg);
   spi_transfer(screen_char);
   x++;
   local_count--;
 }

 spi_transfer(DMDI_reg);
 spi_transfer(END_string);

 spi_transfer(VM0_reg); // turn on screen next vertical
 spi_transfer(ENABLE_display_vert);
 digitalWrite(MAX7456SELECT,HIGH);
}


GekoCH

WOW!

Great! I'm gonna try this for my FPV Plane!
How fast is the refresh rate on the Screen?

Thx
Geko

The_Bongmaster

B-dui in creation.

dfraser

The MAX7456 will display text as soon as you tell it to, or for a cleaner picture you can delay until the next vertical interval.  It can be set for NTSC or PAL. so that would be 60 or 50 times a second.

There seems to be a lot of work with it happening in RC groups from Germany (thank-you babelfish) and I have seen some nice setups with GPS, direction, windspeed, all those good things for fliers.

I'm working next on a sketch that will change the MAX7456's character eeprom to match the ascii character order and content.  That way I can remove the overhead of having a lookup table.

GekoCH

Thx a lot. May you send me the link from the Forum. I can speak German
so it would be easyer for me to read it there.

THx
Geko


zitron

Wow this is awesome! I just saw a post on RCG about the MAX 7456, and was wondering how difficult it would be to get it to work with arduino. Question answered and samples ordered!  :o

Cheers,
-Z-

dfraser

I have another addition to the pack of (hopefully forth coming) MAX7456 utilities.  This one will allow you to change the eeprom font set.  As it comes from Maxim the default burned in fonts are incomplete and in a rather strange order.

default fonts


This means if you are sending ascii data to the arduino for the MAX7456 to print on screen you have to use a rather convoluted lookup table.

So using the MAX7456 evaluation kit software I built this font set and burned it to my MAX7456 osd.

ascii fonts


This set has the proper offset and order for ascii text.

URL for the Maxim evaluation kit software
http://www.maxim-ic.com/tools/evkit/index.cfm?EVKit=558

This utility creates text files that are representative bitmaps of the font set.  Stored in a .mcm file will be 65000+ lines of

01010101
01010101
10101010

This has to be converted to a 'real' value and uploaded to the MAX7456.

01010101 = 0x55 = 4 translucent pixels
01010101 = 0x55 = 4 translucent pixels
10101010 = 0xaa = 4 white pixels

So here is file of the sketch code to do that,
http://home.comcast.net/~ds-fraser/forumimages/MAX7456_font.txt

and the .mcm file I built for the ascii set.
http://home.comcast.net/~ds-fraser/forumimages/ascii_font_set.mcm

When you run the sketch it will first print out the existing font set, wait until you upload the new .mcm file, parse and burn the new set to the MAX7456 and then print that to the screen.

before


after


I hope this is of some use, next I want to be able use the other half of the Eval software (display files) and parse and store the .mdm files.

dfraser

Syvwlch

Looks like Arduino p0wns the MAX7456  ;)

Nicely done!
----------
Mathieu

dfraser

You always think of things you should have added after you hit the 'Post' button.

I used Hyper Terminal to 'Send Text File' in uploading the .mcm files.  It takes about 3min to complete the transfer and burning.

I set the serial speed at 9600, you may be able to go faster but as the MAX7456 takes 12ms to burn each character I thought to play it on the safe side.

Also make sure that at the end of the .mcm text file there is at least one carriage return / blank line.

I think that is all I forgot.

DaveAK

This might appear a dumb question, but how do you get the 'video out' to your TV screen?  How does it hook up if say you have different input sources, (DVD, Sat. recv., XBox, etc.), to overlay on each of those sources?  And what's the purpose of the video in?

(OK, three dumb questions, but all for the price of one!) :)

It looks like a great addition to a current project of mine.

dfraser

The MAX7456 is a composite video overlay device, so you would need to supply some form of video (to the video in).  This could come from a camera, output of a VCR or DVD player.

The Arduino then receives serial data and transmits that to the MAX7456 (via spi) so it can overlay that text on the video signal and output that.

The video signal (with overlay) could then be put to TV set (video in) or recorded to tape.

I hope that answers your question.

dfraser

DaveAK

Thanks!  Answers it perfectly!

CrashingDutchman

Do the characters have a black outline? Maybe it is my eyes, but looking at the picture I don't think they have.

If I am correct, do you know if it is possible to change it so they do have a black outline? The reason I am asking for this is that the characters will be much better readable on light backgrounds.

Go Up