The HAMKit VMAC PiHat features an on-board, tried and trusted,  MAX7456  single-channel monochrome on-screen display (OSD) generator.

The MAX7456 serves all national and international markets with 256 user-programmable characters in both NTSC and PAL standards. The MAX7456 easily displays information such as company logo, custom graphics, time, and date with arbitrary characters and sizes. The MAX7456 is preloaded with 256 characters and pictographs and can be reprogrammed in-circuit using the SPI port.

Using the HAMKit VMAC PiHat video matrix, we are able to take any input to the matrix, map this into the OSD, then take the OSD video output and map to any matrix video output.

Key OSD Features

  • 256 User-Defined Characters or Pictographs in Integrated EEPROM
  • 12 x 18 Pixel Character Size
  • Blinking, Inverse, and Background Control Character Attributes
  • Selectable Brightness by Row
  • Displays Up to 16 Rows x 30 Characters (PAL)
  • Sag Compensation On Video-Driver Output
  • LOS, Active-Low VSYNC, Active-Low HSYNC, and Clock Outputs
  • Internal Sync Generator
  • NTSC and PAL Compatible
  • SPI-Compatible Serial Interface
  • Preprogrammed Character Set

In the below test code

  • Video Input 1, is mapped to the OSD input.  The OSD output is then mapped to Video Out 1
  • Video Input 1, is mapped to the Video Out 2 as a straight thru clean video test
  • The OSD IC is enabled and setup to display a basic test page
  • Any video on input 1 will get super-imposed OSD text and displayed on output 1.  With no OSD on output 2

Example output from the below test code





































# Example of matrix mapping
# Video 1 Input to OSD Input 0x07 0x81 
# OSD Output to Video 1 Output 0x01 0x86
i2cbus.write_byte_data(DEVICE_ADDR, 0x07, 0x81) #Input 1 to OSD In 
i2cbus.write_byte_data(DEVICE_ADDR, 0x01, 0x86) #Input OSD to Output 1

All of our sample and test code is developed in Python.

# HAMKit VMAC PiHat v2.3 - Test OSD - Dave Williams G8PUO
# Setup matrix input and outputs, enable OSD and display a test page
# Video 1 Input to OSD Input 0x07 0x81
# OSD Output to Video 1 Output 0x01 0x86
# Video 1 Input to Video 2 Output 0x03 0x81 (Thru Video Test)
# July 2018.  Thanks to Paul Theunissen, PA5PT, for OSD code template

import smbus
import RPi.GPIO as GPIO
import spidev
import time
from ctypes import *

#====== GPIO


#Define GPIO Outputs
LEDStatus = 22
PTT = 27
OSD_RST = 25

#Setup Outputs

#Initiate LEDs

#====== FMS6501 Matrix

#define values
DEVICE_ADDR = 0x43 #0x43 or 0x03

#setup i2c bus
i2cbus = smbus.SMBus(DEVICE_BUS)

#OSD REset High

def PowerDownPorts():
    print ("PowerDownPorts")
    i2cbus.write_byte_data(DEVICE_ADDR, 0x01, 0x0) #Mute
    i2cbus.write_byte_data(DEVICE_ADDR, 0x03, 0x0) #Mute
    i2cbus.write_byte_data(DEVICE_ADDR, 0x06, 0x0) #Mute
    i2cbus.write_byte_data(DEVICE_ADDR, 0x07, 0x0) #Mute
    i2cbus.write_byte_data(DEVICE_ADDR, 0x08, 0x0) #Mute
    i2cbus.write_byte_data(DEVICE_ADDR, 0x09, 0x0) #Mute

#Disable and Powerdown Matrix Outputs

# Setup Matrix Video Switching
i2cbus.write_byte_data(DEVICE_ADDR, 0x1d, 0xff) #clamp
i2cbus.write_byte_data(DEVICE_ADDR, 0x1e, 0xff) #clamp
i2cbus.write_byte_data(DEVICE_ADDR, 0x07, 0x81) #Input 1 to OSD In
i2cbus.write_byte_data(DEVICE_ADDR, 0x01, 0x86) #Input OSD to Output 1
i2cbus.write_byte_data(DEVICE_ADDR, 0x03, 0x81) #Input 1 to Output 2

#OSD MAX7456
class max7456():
    # Create a SPI
    spi = spidev.SpiDev() 

    # MAX7456 opcodes
    VM0_reg  = 0x00
    VM1_reg  = 0x01
    HOS_reg  = 0x02
    VOS_reg  = 0x03
    DMM_reg  = 0x04
    DMAH     = 0x05
    DMAL     = 0x06
    DMDI     = 0x07
    OSDM     = 0x0C
    RB0      = 0x10
    HOS_reg  = 0x02
    STATUS   = 0xA0

    # PAL - VM0_reg commands
    ENABLE_display      = 0x48
    ENABLE_display_vert = 0x4c
    MAX7456_reset       = 0x42
    DISABLE_display     = 0x40

    # Read command
    READ = 0x80
    MAX_screen_rows = 16

    # White levels
    WHITE_level_80  = 0x03
    WHITE_level_90  = 0x02
    WHITE_level_100 = 0x01
    WHITE_level_120 = 0x00

    chars = {' ':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9,
        '0':10, 'A':11, 'B':12, 'C':13, 'D':14, 'E':15, 'F':16, 'G':17, 'H':18, 'I':19,
        'J':20, 'K':21, 'L':22, 'M':23, 'N':24, 'O':25, 'P':26, 'Q':27, 'R':28, 'S':29,
        'T':30, 'U':31, 'V':32, 'W':33, 'X':34, 'Y':35, 'Z':36, 'a':37, 'b':38, 'c':39,
        'd':40, 'e':41, 'f':42, 'g':43, 'h':44, 'i':45, 'j':46, 'k':47, 'l':48, 'm':49,
        'n':50, 'o':51, 'p':52, 'q':53, 'r':54, 's':55, 't':56, 'u':57, 'v':58, 'x':59,
        'y':60, 'z':61, '(':62, ')':63, '.':64, '?':65, ';':66, ':':67, ',':68, '\'':69,
        '/':70, '"':71, '-':72, '<':73, '>':74, '@':75, '\xa9':76

    def __init__(self):
        # Open a SPI port - max7456 connected on SPI0, 0)
        self.spi.max_speed_hz = 1000000
        self.spi.bits_per_word = 8
        self.spi.cshigh = False
        self.spi.lsbfirst = False
        self.spi.mode = 0
        # On init, reset max7456

        # Set all rows at the same white level
        for x in range (0, self.MAX_screen_rows):
          self.spi.xfer2([(self.RB0 + x), self.WHITE_level_90])

        # Enable max7456
        self.spi.xfer2([self.VM0_reg, self.ENABLE_display]);

    def printStr(self, X, Y, string, enable = True):
        disp = []
        for char in string:

        print (string)

        if enable == False:
            self.spi.xfer([self.VM0_reg, self.Disable_display])
        # Enable 8 bit mode:
        dmm = self.spi.xfer2([self.DMM_reg + self.READ, 0x00])
        dmm = self.setBit(dmm[1], 6)
        self.spi.xfer2([self.DMM_reg, dmm])

        start = X * 30 + Y
        # Clear position
        self.spi.xfer2([self.DMAH, 0x00])
        self.spi.xfer2([self.DMAL, 0x00])

        for char in disp:
            # Write char
            dmah = self.spi.xfer2([self.DMAH + self.READ, 0x00])
            dmah = self.clearBit(dmah[1], 1)
            self.spi.xfer2([self.DMAH, dmah])

            dmah_pos = ((start >> 8) & 0x01)
            dmal = (start & 0xff)
            dmah = dmah | dmah_pos
            start = start + 1

            # Select MSB
            self.spi.xfer2([self.DMAH, dmah])
            self.spi.xfer2([self.DMAL, dmal])
            self.spi.xfer2([self.DMDI, (char)])

    def reset(self):
        self.spi.xfer2([self.VM0_reg, self.MAX7456_reset])
        while True:
            r = self.spi.xfer([self.STATUS, 0x00])
            stable = self.testBit(r[1], 1)
            if stable == 0:
                print ("Reset MAX7456 Ok...")

    def testBit(self, value, offset):
        mask = 1 << value
        return(value & mask)
    def setBit(self, value, offset):
        mask = 1 << offset
        return(value + mask)

    def clearBit(self, int_type, offset):
        mask = ~(1 << offset)
        return(int_type & mask)
    max7456 = max7456()
    #Display OSD Text
    max7456.printStr(2,5, "VMAC Tesing 1 2 3 4", enable = True)
    max7456.printStr(5,5, "OSD Test ABCDEFGHIJ", enable = True)
    max7456.printStr(9,1, "1234567890123456789012345678", enable = True)
    max7456.printStr(13,5, "HAMKit VMAC PiHat v2", enable = True)
except KeyboardInterrupt:

Further details on the configuration and test code can be found in other Wiki pages.  Also see programming and developing in Python.

Leave a Reply

This site uses User Verification plugin to reduce spam. See how your comment data is processed.