Arduino + MAX7456 OSD

On the sparkfun breakout board there are 5 vias underneath the IC. 3 are NC or ground, one is /VSYNC (which you probably won't use) and one is the SCK input. Funny it works out that most of 'em probably won't give you trouble with this IC if they do short to the ground pad.

If you are worried about it, you can always insulate those vias. I typically use a model paint pen for this sort of thing.

I had some SMT components and managed to get nearly all the required discrete components on the breakout board. 0603 SMT parts fit between the 0.1" header pins quite nicely. I've got enough extra parts that I may try a second one and make the whole thing a complete unit, sans wires.

Thanks to dfraser and zitron for posting their code. That sure jump-started my efforts!

-j

Ok, soldered up and ready to go. I do have a really stupid question for you guys. Sorry, I have 10 years of software experience and very little circuit experience. :-[
Radio Shack sells 1/2W, 1/4W and 1/8W resistors. I looked at the data sheet and couldn't find or didn't know how to figure out the right values given what was there. What Wattage resistors should I use, why should I use those, and how do I figure that out next time from the info in the data sheet?
Thanks in advance.

Edited: I think I figured it out. The voltage applied to the circuit is 5V. Looking at the 75 ohm resistor, I would use (5^2)/75 = 25/75 = .3333 W.
So, my resistor would have to be 1/2 W because the 1/4 W (.25) would not be enough. I hope thats right, otherwise, i'm going to feel dumb. :slight_smile:
I think I could use a smaller rated resistor on the video in and video out. I measured 3 analog volts on the BRK and BRL pins from my video breakout board. That gives me .12, so I should be ok with the 1/4.
Please let me know if I am right or wrong on this. I want to be sure before I wire up. Thanks.

You've got the idea.

A couple of things about that particular application: only part of the power is being dissipated by the resistor (the rest is delivered to the chip for Vin and the TV etc. for Vout), plus the video signal is AC so the average power will be a lot less. Between the two you can get by just fine with the 1/8W resistor in that application. (But the 1/4W will work, too!)

-j

When you say that the rest of the power is delivered to the chip, for video in and video out, is there a way to check what that is?
Thanks!

Edit: Well, it worked, kind of. I get my video to display on the tv once I power up my board. I copied the test code for starters and it compiled fine and uploaded to the board with no errors, but no text overlay.

I went over the wiring on the board and it looks like everything on there is wired up right. The only thing else is the serial connection which I double checked and the wires are connected correclty. Am I missing something? It appears to me that the circuit is just for getting Vin and Vout set and that the rest happens internally. So, if I have my video source displaying on the monitor through the board, and, the serial connections correct, all I should have to do is flash the image on the board, right?

I did a little debugging and hooked up some leds and resistors to pins 10, 11, 12 and 13 on the Decimilia and sent High/Low signals to them and got them to flash so those appear to be wired up correctly. I know from other posters that the code works, so I am at a loss for what may be wrong.

Edit: I get the "Ready for text file download" message so I know the program is running. It looks like the 7456 is trying to do something because I get a lot of noise when I run it with the Decimilia hoooked up. So, I either have something screwing up the serial programming of the MAX or I screwed up my soldering. I'll check the soldering on Monday at work.
Thanks for the support so far.

I used the code posted in this thread as well, and it had a serious flicker, so much so that some displays wouldn't sync, and none would sync without a video source. I combined the code from two earlier posts, removed most of it, and made some changes based on my understanding of the datasheet. the result is some rock-solid video that works with or without a source attached to Vin. Maybe it will help. Note it's set to NTSC; you'll have to make some adjustments if you're in a PAL part of the world.

Oh yeah, I ran the sketch that converts the character set so that ASCII is in the correct place before I used the following code.

-j

#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

// video mode register 0 bits
#define VIDEO_BUFFER_DISABLE 0x01
#define MAX7456_RESET 0x02
#define VERTICAL_SYNC_NEXT_VSYNC 0x04
#define OSD_ENABLE 0x08
#define SYNC_MODE_AUTO 0x00
#define SYNC_MODE_INTERNAL 0x30
#define SYNC_MODE_EXTERNAL 0x20
#define VIDEO_MODE_PAL 0x40
#define VIDEO_MODE_NTSC 0x00

// video mode register 1 bits
// duty cycle is on_off
#define BLINK_DUTY_CYCLE_50_50 0x00
#define BLINK_DUTY_CYCLE_33_66 0x01
#define BLINK_DUTY_CYCLE_25_75 0x02
#define BLINK_DUTY_CYCLE_75_25 0x03

// blinking time
#define BLINK_TIME_0 0x00
#define BLINK_TIME_1 0x04
#define BLINK_TIME_2 0x08
#define BLINK_TIME_3 0x0C

// background mode brightness (percent)
#define BACKGROUND_BRIGHTNESS_0 0x00
#define BACKGROUND_BRIGHTNESS_7 0x01
#define BACKGROUND_BRIGHTNESS_14 0x02
#define BACKGROUND_BRIGHTNESS_21 0x03
#define BACKGROUND_BRIGHTNESS_28 0x04
#define BACKGROUND_BRIGHTNESS_35 0x05
#define BACKGROUND_BRIGHTNESS_42 0x06
#define BACKGROUND_BRIGHTNESS_49 0x07

#define BACKGROUND_MODE_GRAY 0x40

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


#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



//////////////////////////////////////////////////////////////
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(VERTICAL_SYNC_NEXT_VSYNC|OSD_ENABLE);
  digitalWrite(MAX7456SELECT,HIGH);
  delay(100);

  spi_transfer(VM1_reg);
  spi_transfer(BLINK_DUTY_CYCLE_50_50|BLINK_TIME_3);
  digitalWrite(MAX7456SELECT,HIGH);

  Serial.println("OSD starting");
  delay(100);  
  
}
 
 
 /////////////////////////////////////////////////////////////
void loop()
{
  OSD_write_to_screen("Hello world", 3, 5, 0,0);
  OSD_write_to_screen("Hello world", 5, 7, 0,1);
  OSD_write_to_screen("Hello world", 7, 9, 1,0);
  OSD_write_to_screen("Hello world", 9, 11, 1,1);
  
  delay(5000);
  

  spi_transfer(VM1_reg);
  spi_transfer(BLINK_DUTY_CYCLE_75_25);
  digitalWrite(MAX7456SELECT,HIGH);
  delay(100);  

  OSD_write_to_screen("DUTY_CYCLE_75_25", 3, 5, 0,0);
  OSD_write_to_screen("Hello world", 5, 7, 0,1);
  OSD_write_to_screen("Hello world", 7, 9, 1,0);
  OSD_write_to_screen("Hello world", 9, 11, 1,1);

  delay(5000);
  
  spi_transfer(VM1_reg);
  spi_transfer(BLINK_DUTY_CYCLE_25_75);
  digitalWrite(MAX7456SELECT,HIGH);
  delay(100);  

  OSD_write_to_screen("DUTY_CYCLE_25_75", 3, 5, 0,0);
  OSD_write_to_screen("Hello world", 5, 7, 0,1);
  OSD_write_to_screen("Hello world", 7, 9, 1,0);
  OSD_write_to_screen("Hello world", 9, 11, 1,1);

  delay(5000);
  
  spi_transfer(VM0_reg);
  spi_transfer(VERTICAL_SYNC_NEXT_VSYNC|OSD_ENABLE|SYNC_MODE_INTERNAL);
  digitalWrite(MAX7456SELECT,HIGH);
  delay(100);

  OSD_write_to_screen("internal sync   ", 3, 5, 0,0);
  OSD_write_to_screen("Hello world", 5, 7, 0,1);
  OSD_write_to_screen("Hello world", 7, 9, 1,0);
  OSD_write_to_screen("Hello world", 9, 11, 1,1);

  delay(5000);
  
  spi_transfer(VM0_reg);
  spi_transfer(VERTICAL_SYNC_NEXT_VSYNC|OSD_ENABLE|SYNC_MODE_EXTERNAL);
  digitalWrite(MAX7456SELECT,HIGH);
  delay(100);

  OSD_write_to_screen("external sync", 3, 5, 0,0);
  OSD_write_to_screen("Hello world", 5, 7, 0,1);
  OSD_write_to_screen("Hello world", 7, 9, 1,0);
  OSD_write_to_screen("Hello world", 9, 11, 1,1);

  delay(5000);
  
  spi_transfer(VM0_reg);
  spi_transfer(VERTICAL_SYNC_NEXT_VSYNC|OSD_ENABLE|SYNC_MODE_AUTO);
  digitalWrite(MAX7456SELECT,HIGH);
  delay(100);

  OSD_write_to_screen("auto sync     ", 3, 5, 0,0);
  OSD_write_to_screen("Hello world", 5, 7, 0,1);
  OSD_write_to_screen("Hello world", 7, 9, 1,0);
  OSD_write_to_screen("Hello world", 9, 11, 1,1);

  delay(5000);
  
}

//////////////////////////////////////////////////////////////
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
}


 
// ============================================================   WRITE TO SCREEN
void OSD_write_to_screen(const char s[], byte x, byte y, byte blink, byte invert){
  unsigned int linepos;
  byte local_count;
  byte settings, char_address_hi, char_address_lo;
  byte screen_char;

  local_count = 0;

  char_address_hi = 0;
  char_address_lo = 0;
    
  // convert x,y to line position
  linepos = y*30+x;
  
  // divide in to hi & lo byte
  char_address_hi = linepos >> 8;
  char_address_lo = linepos;
  
  // clear the screen
  //OSD_clear();
  
  settings = B00000001;
  
  // set blink bit
  if (blink) {
    settings |= (1 << 4);       // forces nth bit of x to be 1.  all other bits left alone.
    //x &= ~(1 << n);      // forces nth bit of x to be 0.  all other bits left alone.  
  }
  // set invert bit
  if (invert){
    settings |= (1 << 3);       // forces nth bit of x to be 1.  all other bits left alone.
  }

  
  digitalWrite(MAX7456SELECT,LOW);

  spi_transfer(DMM_reg); //dmm
  spi_transfer(settings);

  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);
  
  
  while(s[local_count]!='\0') // write out full screen
  {
    screen_char = s[local_count];
    spi_transfer(DMDI_reg);
    spi_transfer(screen_char);
    local_count++;
  }
  
  spi_transfer(DMDI_reg);
  spi_transfer(END_string);
  
  spi_transfer(DMM_reg); //dmm
  spi_transfer(B00000000);

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

Ok, first, sorry for all the posts. :slight_smile:
I did some more debugging and it looks like its working after all, sort of.

If I turn off my video source, the overlay text shows up on the monitor. If I turn on the video source, the text disappears, but when I hold down the reset button on the Decimilia, the text shows up. If I move around the caps on the video out side, the text shows up but goes away when I let go. If I turn the video source off again, the text shows up and stays. ???????

At first I thought it was a bad cap or a loose connection, but, if I turn the video source off, the text shows up and stays.

Edit: I'll give your code a try and see what happens. Im in California so its NTSC for me. Thanks!

you could have a loose connection (or poor contacts if on a breadboard). I also had bad luck with a single 470uF polarized electrolytic cap on Vout (that was the only match I had). I went back and changed to a 10uF on Vout and a 10uF on Csag and it worked a lot better.

-j

I tried your file and I got much better results. At least now the text stays on the screen. I still have some noise but I think that may just be the way everything is plugged into the bread board or the soldering of the video lines into the header pins. I'll go back to the RCA jack breakout board and see if that cleans up the picture. Next i'll solder the parts onto a PC board that I bought. That should tighten up the connections some. I'll spend the afternoon comparing your code to the original code to see if I can figure out what may be making a difference. It may all come back to my soldering skills. :slight_smile:
Thanks!

I used the code posted in this thread as well, and it had a serious flicker, so much so that some displays wouldn't sync, and none would sync without a video source.

Hmm... dfraser's code worked perfectly for me. There were some flickers when you try to output text very fast, because there was a "clear screen command" in the original "write_to_screen" function. I took it out in my own function because I needed to, well, write to screen fast.

saccrue, you only need a 1/4w resistor because the voltage of composite video is only 1V max, not 5V. 1^2/75 is very small! If you have noise issues you may also look at if your video and power ground are properly connected. The board should output perfectly fine even without a input source in default.

Cheers,
-Z-

Hmm... dfraser's code worked perfectly for me. There were some flickers when you try to output text very fast, because there was a "clear screen command" in the original "write_to_screen" function. I took it out in my own function because I needed to, well, write to screen fast.

I wondered about that, but I made 4 calls to your OSD_write_to_screen() to write 4 separate lines, then went to sleep (er, into an infinite loop to prevent further processing). Same result - nasty flicker.

One thing I know I changed was to set the flag to delay text change until next vertical sync (instead of immediately). From what I understand this should only matter when text is changing, though.

-j

Yeah, I attribute the noise to my soldering and breadboard. :slight_smile:
I cut the end of my cable off and soldered the two wires to two pins of a male header and stuck it into the breadboard. I went back the the composite breakout board and the noise got better. Sometimes its perfect and sometimes its noisy. Now that I have it working, i'm going to solder the parts down on a PC board and make the connections better. I'll probably go in over the holiday and do the soldering at work and take my time with it. Right now i'm going over the software and the datasheet again.
Thanks for the info on the composite voltage, that makes sense. I've been doing a lot of reading and things are starting to fall into place.

While I've used the Sparkfun SSOP header board for my past MAX7456 projects I've thought that there should be a better solution. Not finding one I tried to make my own. This is my first try with Eagle and there is most likely a lot wrong with it. If anyone wants to poke at it and make any improvements please feel free.

I picked a 24pin dip size as the carrier because that size socket is more readily available, and there are only 20 active pins on the MAX7456 anyway.

I also added 'through hole' .1uf filter caps and the 27Meg crystal. These were the components I felt got the most benefit from being physically close to the IC chip.

MAX7456 Header Pinout

24pin dip MAX7456 SSOP
1 clkout 7
2 cs 8
3 sdin 9
4 sclk 10
5 sdout 11
6 gnd
7 gnd
8 los 12
9 vsync 17
10 hsync 18
11 reset 19
12 nc
13 vin 22
14 nc
15 nc
16 sag 25
17 vout 26
18 +5
19 +5
20 nc
21 nc
22 nc
23 nc
24 clkin 5

Download the Eagle board file here
http://home.comcast.net/~ds-fraser/forumimages/MAX7456_header.brd

Hello I am new to this please inform me what IDE use to get the hex for Atmega,thanks.

hello guys!

do you think a arduino nano might work on this project?

thanks a lot!

Tony

hello!

I am very interested on this OSD, to make an FPV OSD, and the links below are not working any more:

http://home.comcast.net/~ds-fraser/forumimages/max7456_shield.bmp

http://home.comcast.net/~ds-fraser/forumimages/MAX7456_header.brd
http://home.comcast.net/~ds-fraser/forumimages/mdm_arduino.zip
http://home.comcast.net/~ds-fraser/forumimages/MAX7456_font.txt
http://home.comcast.net/~ds-fraser/forumimages/ascii_font_set.mcm

And google did not cache them.... so if anyone has them.... please send it to me...

I also did not understand the process of sendning the new mcm files to the MAX 7456....

you can email me at moraes_gyn@hotmail.com

thanks in advance,

Tony

And google did not cache them.... so if anyone has them.... please send it to me...

I have a few, will try to get them together and email them shortly. I don't have the images or schematics.

I also did not understand the process of sendning the new mcm files to the MAX 7456...

You run the special sketch on the arduino, then transfer the contents of the mcm file (just an ASCII text file) to the arduino. The sketch receives the data, converts it, and transfers it to the character flash storage on the MAX7456. You do this one time for each 7456.

-j

Sorry about the files and pictures. I changed ISP a couple of months ago and sort of forgot that I had been hosting that information. I have created a new page with a ZIP file of all the MAX7456 stuff I had. So if anyone wants it, go here.

http://mysite.verizon.net/res12f4fa/index.html

dfraser

Thx dfraser!

ok - stupid newbie time.

I've got the SF MAX7456 breakout. It seems to be just about a built verision of the first post.

I am hooking this to an Arduino Mega and am using the sketch from dfrasers new site (thanks!).

I've modified the defines to map to the Mega's relocated MOSI etc pins (50-53) and hooked up the breakout and run vsync to pin 2.

I also switched it around to work for PAL (by uncommenting the defines and commenting out the NTSC ones)

with the sketch compiled and uploaded, I get the "waiting for text file" but no matter what i enter between []'s it doesn't display. I've also uncommented the serial prints and it outputs the appropriate functions at each step...

video does pass through, but it will do that even with the board just powered and no other pins connected.

also, when its hooked up, after a few seconds i lose picture, but touching the breakout board near the gnd pin will generally bring it back.

Do i need to shield this board more or something?

anyway, any advice or suggestions would be appreciated. I dont have any pullups or caps installed, since it seems to be all on the board...

have i missed something obvious ? im pretty sure its hardware and not software, but just in case, heres the source including what i modified:

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

#define DATAOUT 51//MOSI
#define DATAIN  50//MISO
#define SPICLOCK  52//sck
#define MAX7456SELECT 53//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
//]
//
//examples
//**
//[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
//
//
//////////////////////////////////////////////////////////////
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);  
}
Loop snipped

Any assistance would be greatly appreciated....

Adrian