An On-Screen-Display with only 5 components!
Overview
An On-Screen-Display with only 5 components! Pushes an 8-bit PIC to the limits to build a cheap PAL video superimposer: MCU extracts PAL sync in real-time and overlays a line of text to the video.
How it works
PIC12F683 overclocked at 25 MHz (from 20 MHz max). Uses PIC internal comparator for horizontal sync detection, internal voltage reference for PAL clip level (625mV). No signal mixer: output directly added to input signal.
Inline assembly macros (SETPIXEL, WRITECHAR) for bit-banging video output. ISR handles comparator interrupts for sync detection and pixel output.
Demo features: welcome messages, vertical scrolling, horizontal scrolling, text resize, color change, RTC display.
Circuit Schematic
- PIC12 MCU at 25 MHz.
- Direct signal addition for video overlay.
C Source Code
/* ******************************************************************************* * pico OSD : simple PIC OSD PAL video superimposer ******************************************************************************* * * This program shows how to superimpose a text on a PAL composite video signal * with a PIC and only 4 resistors. * * source code for mikro C compiler V7.0.0.3 * feel free to use this code at your own risks * * target : PIC12F683 @25MHz * HS clock, no watchdog. * ******************************************************************************* */ #include "built_in.h" #include "fonts.h" /************************* * CONSTANTS DEFINITIONS *************************/ /* * CVREF = (VR<3:0>/24) x VDD * PAL CLIP LEVEL : 0.625 V */ #define CLIP_VOLTAGE 625 // in mV #define CLIP_LEVEL (CLIP_VOLTAGE * 24 / 5000) #define OUT GPIO.F2 // video output #define HSYMBOLS 11 // number of horizontal symbols #define FONTH 7 // pixel font height #define T0FREQ (Clock_kHz() * 1000 / 4) #define T028us (unsigned char)(T0FREQ * 0.000028) /************************ * PSEUDO FUNCTIONS ************************/ /* * set message display from line l, color c (1=white, 0=black), font size s */ #define setMsg(l, c, s) vStart = l ; vStop = vStart + FONTH * s ; lHeight = s ; OUT = c /* * set output depending on bit number x of the current position indexed by FSR * Note : if you use a 20 Mhz crystal instead of 25 Mhz, remove NOP */ #define SETPIXEL(x) asm { BTFSC INDF, x } \ asm { BCF TRISIO, 2 } \ asm { NOP } \ asm { BSF TRISIO, 2 } /* * write the 5 bits of the current character, then increment FSR */ #define WRITECHAR() SETPIXEL(0) \ SETPIXEL(1) \ SETPIXEL(2) \ SETPIXEL(3) \ SETPIXEL(4) \ FSR++ /*********************** * VARIABLES DEFINITIONS ***********************/ const unsigned char scroll_msg[] = "Scroll text : pico OSD is scrolling this very long message on your screen, and then will rewind it very fast. Ready ? Now !" ; unsigned char line = 0 ; // current line number unsigned char ctrLine = 0 ; // counter of line to be repeated unsigned char bm[FONTH][HSYMBOLS] absolute 0x23 ; // bitmap to superimpose unsigned char display absolute 0xa0 ; // address of display data unsigned char msg[HSYMBOLS + 1] ; // dynamic string volatile unsigned char vStart = 255 absolute 0x20, // vertical start line vStop absolute 0x21, // vertical stop line lHeight absolute 0x22 ; // line height unsigned int frm ; // frame counter unsigned char sec = 0, // RTC seconds, mn = 0, // minutes, hours = 0 ; // hours /**************************** * Interrupt Service routine ****************************/ void interrupt() { if(CMCON0.COUT) // comparator output high { if((line >= vStart) && (line < vStop)) // in display window { FSR = display ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; ctrLine++ ; if(ctrLine == lHeight) { display += HSYMBOLS ; ctrLine = 0 ; } TMR0 = 0 ; } else if(TMR0 >= T028us) // new frame sync { line = 0 ; ctrLine = 0 ; display = (char)bm ; } } else // comparator output low { TMR0 = 0 ; if(line < 254) { line++ ; } /* * Real Time Clock */ frm++ ; if(frm == 625 * 25) // 25 frames of 625 lines/sec { frm = 0 ; sec++ ; if(sec >= 60) { sec = 0 ; mn++ ; if(mn >= 60) { mn = 0 ; hours++ ; } } } } PIR1.CMIF = 0 ; } /* * update display bitmap with character c at column pos */ void updatechar(unsigned char c, unsigned char pos) { unsigned char py, col ; if(c < 32) c = 32 ; else if(c > 128) c = 32 ; else c -= 32 ; for(col = 0 ; col < 5 ; col++) { unsigned char fnt = fonts[c * 5 + col] ; unsigned char mask = 1 << col ; for(py = 0 ; py < FONTH ; py++) { if(fnt.F0) { bm[py][pos] |= mask ; } else { bm[py][pos] &= ~mask ; } fnt >>= 1 ; } } } /* * update display with constant string m at offset o */ void updateMsg(const char *m, unsigned char o) { unsigned char n, l, c ; l = 0 ; while(m[l++]) ; for(n = 0 ; n < HSYMBOLS ; n++) { c = m[o++] ; o %= l ; updateChar(c, n) ; } } /* * 10 ms delay, c times */ void delay10ms(unsigned char c) { do { Delay_ms(10) ; } while(--c) ; } /* * program entry */ void main() { unsigned char i ; GPIO = 0b100 ; // GP2 set to one (white text) TRISIO = 0b110 ; // GP1 video in, GP2 video out CMCON0 = 0b10100 ; // comparator, internal voltage ref VRCON = 0b10100000 | CLIP_LEVEL ; // voltage reference, low level ANSEL = 0b10 ; // GP1 as analog OPTION_REG = 0b10001000 ; // no prescaler on TMR0 PIE1.CMIE = 1 ; // enable comparator interrupt INTCON.PEIE = 1 ; INTCON.GIE = 1 ; lHeight = 1 ; vStart = 10 ; vStop = 10 ; /************************* * pico OSD demonstration *************************/ /* welcome messages */ updateMsg("Welcome to ", 0) ; for(i = 1 ; i < 10 ; i++) { setMsg(50, 1, i) ; delay10ms(5) ; } delay10ms(100) ; updateMsg(" pico OSD ", 0) ; setMsg(100, 1, 6) ; delay10ms(100) ; updateMsg("the tiniest", 0) ; setMsg(100, 1, 6) ; delay10ms(100) ; updateMsg("OSD of the ", 0) ; setMsg(100, 1, 6) ; delay10ms(100) ; updateMsg("WORLD !!!!!", 0) ; setMsg(100, 1, 6) ; delay10ms(500) ; updateMsg(" It can... ", 0) ; setMsg(100, 1, 6) ; delay10ms(500) ; /* move message along vertical axis */ updateMsg(" Move text ", 0) ; for(i = 20 ; i < 195 ; i++) { setMsg(i, 1, 6) ; delay10ms(1) ; } for( ; i > 20 ; i--) { setMsg(i, 1, 6) ; delay10ms(1) ; } for( ; i < 100 ; i++) { setMsg(i, 1, 6) ; delay10ms(1) ; } delay10ms(100) ; /* horizontal scroll */ updateMsg(scroll_msg, 0) ; delay10ms(200) ; for(i = 0 ; i < sizeof(scroll_msg) - HSYMBOLS ; i++) { updateMsg(scroll_msg, i) ; setMsg(100, 1, 6) ; delay10ms(10) ; } delay10ms(50) ; for( ; i > 0 ; i--) { updateMsg(scroll_msg, i) ; setMsg(100, 1, 6) ; delay10ms(3) ; } updateMsg(scroll_msg, 0) ; delay10ms(100) ; /* change text size */ updateMsg("Resize text", 0) ; delay10ms(100) ; for(i = 6 ; i < 20 ; i++) { setMsg(100, 1, i) ; delay10ms(10) ; } for( ; i > 0 ; i--) { setMsg(100, 1, i) ; delay10ms(10) ; } for(i = 1 ; i < 6 ; i++) { setMsg(100, 1, i) ; delay10ms(10) ; } delay10ms(100) ; /* change text color */ for(i = 0 ; i < 2 ; i++) { updateMsg(" In Black ", 0) ; setMsg(100, 0, 6) ; delay10ms(100) ; updateMsg(" Or White ", 0) ; setMsg(100, 1, 6) ; delay10ms(100) ; } /* prepare to display run time */ updateMsg("Run time : ", 0) ; setMsg(100, 1, 6) ; delay10ms(100) ; setMsg(40, 1, 3) ; delay10ms(100) ; updateChar('H', 2) ; updateChar('m', 5) ; updateChar('i', 6) ; updateChar('n', 7) ; updateChar('s', 10) ; /* update run time clock display */ for(;;) { updateChar(hours / 10 + '0', 0) ; updateChar(hours % 10 + '0', 1) ; updateChar(mn / 10 + '0', 3) ; updateChar(mn % 10 + '0', 4) ; updateChar(sec / 10 + '0', 8) ; updateChar(sec % 10 + '0', 9) ; } }
Download project
PicoOSD project files
Download PicoOSD-project.zip for mikroC PRO for PIC12F683:
Includes:
- mikroC PRO project files for PIC12F683
- C source code (~400 lines)
- HEX files