www.micro-examples.com : PicoOSD

Views
From www.micro-examples.com
Revision as of 15:49, 9 February 2012 by Brunog (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

An On-Screen-Display with only 5 components !

Contents

PicoOSD : a PIC-based simple and cheap OSD

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.

Circuit Schematic

PicoOSD-schematic.jpg

  • The PIC12 MCU is slightly overclocked : 25 MHz instead of 20 MHz max
  • There is no signal mixer : output signal is directly added to input signal, not very clean but fine for the demo

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.
 *
 * 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 project

Download PicoOSD-project.zip file for mikroC : File:PicoOSD-project.zip

Includes :

  • mikroC PRO project files for PIC12F683, should work also with most of PIC
  • picoOSD C source code
  • picoOSD .HEX files

Discussion and comments

You need JavaScript enabled for viewing comments

Navigation
Others
Donation
You can help :
with Paypal
Share
Personal tools
www.micro-examples.com Electronic circuits with micro-controllers, projects with source code examples