(→C Source code) |
|||
(4 intermediate revisions by one user not shown) | |||
Line 1: | Line 1: | ||
+ | [[File:PicoOSD.jpg|thumb|PicoOSD components]] | ||
An On-Screen-Display with only 5 components ! | An On-Screen-Display with only 5 components ! | ||
==PicoOSD : a PIC-based simple and cheap OSD== | ==PicoOSD : a PIC-based simple and cheap OSD== | ||
− | + | A short video clip is sometimes better than a long explanation : | |
{{#ev:youtube|u1N5WJ_dZ3s}} | {{#ev:youtube|u1N5WJ_dZ3s}} | ||
− | 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 | + | 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 overlays a line of 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. | If you like it, please see also my [[Pic Pal Video Library]], to turn a [[PIC18]] into a black and white PAL video generator. | ||
Line 451: | Line 452: | ||
==Discussion and comments== | ==Discussion and comments== | ||
+ | {{#w4grb_rate:}} | ||
<discussion /> | <discussion /> |
An On-Screen-Display with only 5 components !
Contents |
A short video clip is sometimes better than a long explanation :
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 overlays a line of 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 :
To superimpose text to a PAL video signal, we need to control timing with precision to get a stable picture. We have to deal with vertical synchronization, which tells us when a new frame starts, and with horizontal synchronization, which tells us when a new line starts.
Usually, and external circuit is used to extract both vertical and horizontal synchronization pulses from the PAL video signal, the LM1881 integrated circuit does it very well for example.
Since I wanted to have a very simple circuit, I had to find a way to make the PIC do this job.
First, we must be able to know when a video line starts : we will use the PIC internal comparator module to do it. The internal voltage reference module will be programmed with a voltage clip level, the comparator will then trigger an interrupt each time the input voltage will become lower or higher than the clip level. This will be our horizontal sync separator.
Second, we must be able to know when a frame starts do get vertical sync : PAL signal uses special sync pulses to announce a new frame. We have to detect a 28 µs low level pulse, there are five of them in the vertical sync and none elsewhere. The internal timer module of the PIC will be used to count time of low level pulses.
This done, we must be able to know what to superimpose to the video signal. A bitmap representation of the text to be displayed is built in RAM from a 5x7 fonts table. On each new line interrupt, we check if we are in display time window for adding pixels or not.
To add a pixel to the video signal, we change output pin from high Z state to output, the output then imposes +Vcc or 0V to display either a white or a black pixel. The result is a superimposed text on transparent background .
Download PicoOSD-project.zip file for mikroC : File:PicoOSD-project.zip
Includes :
Current user rating: 92/100 (69 votes)
powered by commenterra | Recent comments |