An On-Screen-Display with only 5 components !
Contents |
Let me introduce PicoOSD to you with this short video clip :
The idea of this circuit is to push an 8-bit PIC to the limits to build a cheap video superimposer : MCU extracts PAL sync in real-time and add a line text to the video.
If you like it, please see also my Pic Pal Video Library, to turn a PIC18 into a black and white PAL video generator.
/* ******************************************************************************* * 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. * * Author : Bruno Gavand, October 2007 * see more details on http://www.micro-examples.com/ * ******************************************************************************* */ #include "built_in.h" #include "fonts.h" /************************* * CONSTANTS DEFINITIONS *************************/ /* * CVREF = (VR<3:0>/24) × VDD * PAL CLIP LEVEL : 0.625 V */ #define CLIP_VOLTAGE 625 // in mV #define CLIP_LEVEL (CLIP_VOLTAGE * 24 / 5000) // for VR bits CMCON<3:0> #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) // number of TMR0 ticks in one second #define T028us (unsigned char)(T0FREQ * 0.000028) // number of TMR0 ticks in 28 µs /************************ * PSEUDO FUNCTIONS ************************/ /* * this macro definition sets 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 to by FSR * Note : if you use a 20 Mhz crystal instead of a 25 Mhz crystal, remove NOP */ #define SETPIXEL(x) asm { BTFSC INDF, x ; skip next intruction if bit x is set } \ asm { BCF TRISIO, 2 ; set GP2 as output and superimpose pixel } \ asm { NOP ; no operation (enlarges pixel) } \ asm { BSF TRISIO, 2 ; set GP2 as high Z input (no pixel superimposed) } /* * write the 5 bits of the current character in the current line, * then increment FSR to point to the next character */ #define WRITECHAR() SETPIXEL(0) \ SETPIXEL(1) \ SETPIXEL(2) \ SETPIXEL(3) \ SETPIXEL(4) \ FSR++ /*********************** * VARIABLES DEFINITIONS ***********************/ /* * demo message to be scrolled */ 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 (to make big sized fonts) unsigned char bm[FONTH][HSYMBOLS] absolute 0x23 ; // bitmap to be superimposed to video unsigned char display absolute 0xa0 ; // address of data to be displayed, ensure it is in the same memory bank as TRISIO unsigned char msg[HSYMBOLS + 1] ; // dynamic string to be displayed volatile unsigned char vStart = 255 absolute 0x20, // vertical start : first line to be superimposed vStop absolute 0x21, // vertical stop : last line to be superimposed lHeight absolute 0x22 ; // line height : number of time the same line must be displayed unsigned int frm ; // frame counter unsigned char sec = 0, // RTC seconds, mn = 0, // minutes, hours = 0 ; // hours /**************************** * Interrupt Service routine ****************************/ /* * since no other interrupts but CMIE are enabled, no need to check this flag. */ void interrupt() { if(CMCON0.COUT) // if comparator output changed to high { if((line >= vStart) && (line < vStop)) // if we are in display window { FSR = display ; // load FSR with current bitmap address WRITECHAR() ; // set character pixels WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; WRITECHAR() ; ctrLine++ ; // increment same line counter if(ctrLine == lHeight) // do we have to change ? { display += HSYMBOLS ; // yes, change display location to next bitmap row ctrLine = 0 ; // reset counter } TMR0 = 0 ; // keep timer 0 clear to prevent false start of frame detection } else if(TMR0 >= T028us) // if, check low pulse duration { line = 0 ; // we are in a new frame sync, reset line counter ctrLine = 0 ; // reset same line counter display = (char)bm ; // points to the bitmap start } } else // compatator output changed to low { TMR0 = 0 ; // clear timer 0 if(line < 254) // if we can { line++ ; // increment line counter (only first 255 lines are used) } /* * Real Time Clock */ frm++ ; // increment frames counter if(frm == 625 * 25) // PAL is made of 25 frames of 625 lines per second { frm = 0 ; // clear counter sec++ ; // increment seconds if(sec >= 60) // check for seconds rollover { sec = 0 ; // clear seconds mn++ ; // increment minutes if(mn >= 60) // check for minutes rollover { mn = 0 ; // clear minutes hours++ ; // increment hours } } } } PIR1.CMIF = 0 ; // clear comparator interrupt flag } /* * update display bitmap with character c at column pos */ void updatechar(unsigned char c, unsigned char pos) { unsigned char py, col ; /* * check for under/overflow */ if(c < 32) c = 32 ; else if(c > 128) c = 32 ; else c -= 32 ; // control characters under space in ASCII table are not displayed for(col = 0 ; col < 5 ; col++) // for each character columns { unsigned char fnt = fonts[c * 5 + col] ; // get bitmap font unsigned char mask = 1 << col ; // build bitmap mask for(py = 0 ; py < FONTH ; py++) // for each character lines { if(fnt.F0) { bm[py][pos] |= mask ; // set pixel } else { bm[py][pos] &= ~mask ; // clear pixel } fnt >>= 1 ; // shift bitmap mask } } } /* * update display message with constant string pointed to by m with offset o within the string */ void updateMsg(const char *m, unsigned char o) { unsigned char n, l, c ; /* * get string length */ l = 0 ; while(m[l++]) ; for(n = 0 ; n < HSYMBOLS ; n++) // for all symbols { c = m[o++] ; // get character o %= l ; // circularize string updateChar(c, n) ; // put character to display bitmap } } /* * 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 is set to one be default (text is superimposed in white) TRISIO = 0b110 ; // GP1 is video input, GP2 is video output (set to high Z first) CMCON0 = 0b10100 ; // comparator module : no output, uses internal voltage reference VRCON = 0b10100000 | CLIP_LEVEL ; // voltage reference module : inverted output, low level ANSEL = 0b10 ; // all pins but GP1 (comparator input) as digital OPTION_REG = 0b10001000 ; // no prescaler on TMR0 PIE1.CMIE = 1 ; // enable comparator interrupt INTCON.PEIE = 1 ; // enable peripheral interrupt INTCON.GIE = 1 ; // enable global interrupt /* * init display window */ 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 axe */ 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) ; } }
As you can see, there is no assembly in C source code but the SETPIXEL(c) macro. Video synchronization and generation is made by interrupt routine, main loop only controls shape and position of the text.
Download PicoOSD-project.zip file for mikroC : File:PicoOSD-project.zip
Includes :
powered by commenterra | Recent comments |