VMAC PiHat v2 OSD
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 #!/usr/bin/python import smbus import RPi.GPIO as GPIO import spidev import time from ctypes import * #====== GPIO GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) #GPIO.cleanup() #Define GPIO Outputs LEDStatus = 22 PTT = 27 OSD_RST = 25 #Setup Outputs GPIO.setup(LEDStatus,GPIO.OUT) GPIO.setup(PTT,GPIO.OUT) GPIO.setup(OSD_RST,GPIO.OUT) #Initiate LEDs GPIO.output(LEDStatus,GPIO.HIGH) GPIO.output(PTT,GPIO.HIGH) time.sleep(0.5) GPIO.output(LEDStatus,GPIO.LOW) GPIO.output(PTT,GPIO.LOW) time.sleep(0.5) #====== FMS6501 Matrix #define values DEVICE_BUS = 1 DEVICE_ADDR = 0x43 #0x43 or 0x03 #setup i2c bus i2cbus = smbus.SMBus(DEVICE_BUS) #OSD REset High GPIO.output(OSD_RST,GPIO.HIGH) def PowerDownPorts(): print ("PowerDownPorts") i2cbus.write_byte_data(DEVICE_ADDR, 0x01, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADDR, 0x03, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADDR, 0x06, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADDR, 0x07, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADDR, 0x08, 0x0) #Mute time.sleep(.01) i2cbus.write_byte_data(DEVICE_ADDR, 0x09, 0x0) #Mute time.sleep(.01) #Disable and Powerdown Matrix Outputs PowerDownPorts() # 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 self.spi.open(0, 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 self.reset() # 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: disp.append(self.chars[char]) 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]) time.sleep(0.1) while True: r = self.spi.xfer([self.STATUS, 0x00]) stable = self.testBit(r[1], 1) if stable == 0: print ("Reset MAX7456 Ok...") break break 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) try: 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: spi.close()
Further details on the configuration and test code can be found in other Wiki pages. Also see programming and developing in Python.