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 |