How to build a simple and low-cost oscilloscope with a text LCD ?
Contents |
Let me introduce LCDscope to you with this short video clip :
The idea of this circuit is use text LCD programmable characters to display a signal.
FORUMS Microcontrollers Project Examples NEW:: picoDetector : How to detect metal with a PIC picoBAT, an ultrasonic bat detector with 3 components LCDscope, not a GLCD but a text LCD oscilloscope TouchClock : Design your own GLCD Clock Coming Soon : Ethernal clock, a digital SNTP clock with embedded web server Pico OSD, a PIC video superimposer PicOClock, a PIC Oscilloscope Clock A Universal Advanced Keypad Library A PIC16F84A Alarm Clock Binary File to C, Basic and Pascal Converter PIC16F877A Thermometer with MCP9700A sensor Mysterious Opcodes in PIC16 Instruction Set ! A Voice Controlled LED Light Show PIC PAL Video Library The Secret Functions of MikroC Ethernet Library for ENC28J60 A Cheap Ultrasonic Range Finder A PIC16F819 DYMOCLOCK PIC16F84A MemoSound Game EasyPic3 Programming Status LED EasyPic2 with on-board Ethernet Adapter C, Pascal & Basic to ASM translator Automatic LED display dimmer PIC PWM Calculator & Code Generator Simple & Cheap Thermometer Multiple non-blocking delays with 1 timer DCF-77 PIC LED clock Simple Frequency Meter Single-Tube nixie clock EasyPic2 programming status LED PIC FAQs PIC .HEX Test Files Free Download LED Blinking Example VR Stamp development kit by mikroElektronika Sudoku Solver Online Shop Site & Web Search Links About the Author
Did you find this site useful ? Please
to help www.micro-examples.com
If you need a coder or a freelance programmer, submit your project to me
Demandez à Google de traduire cette page en français
LCDscope, not a GLCD but a text LCD oscilloscope
Printer-friendly version | Forums | FAQs |
LCDSCOPE VIDEO CLIP
The 20x4 text LCD with yellow backlight you can see in the video comes from CircuitED, please visit :
http://www.circuit-ed.com/20x4-BlkYel-Character-LCD---77x47mm-P179C8.aspx
to get more details about this part. Thank you Warren !
Also works with standard 16x2 LCD :
Download LCDscope project Download LCDscope_project.ZIP file for mikroC
Includes :
mikroC project files for PIC18F452, should work also with most of PIC LCDscope C source code .HEX files Circuit schematic Circuit schematic
C source code
/*
******************************************************************************* * LCDscope : a PIC18 oscilloscope on a 20x4 text LCD * also works with a standard 16x4 text LCD ******************************************************************************* * * This program shows how to use custom chars on a text LCD to build a tiny graphic screen * To illustrate this, here is a quick & dirty mini oscilloscope. * * Author : Bruno Gavand, February 2009 * see more details on http://www.micro-examples.com/ * * source code for mikro C compiler V8.2 * feel free to use this code at your own risks * * target : PIC18 with 10 Mhz crystal, HS PLL * * PIC PIN Assignemnt : * * RA0 : analog input, 0-5V * * control buttons on PORTB (internal pull-ups, switchs to GND) : * RB0/RB1 : change horizontal frequency * RB2/RB3 : change input range * RB4 : hold/release screen * * 4-bit LCD on PORTD : * RD2 : RS * RD3 : E * RD4 : D4 * RD5 : D5 * RD6 : D6 * RD7 : D7 * Note : R/W pin of LCD must be tied to ground. * * Credits to CircuitED for the 20x4 black/yellow LCD * http://www.circuit-ed.com/ * Thank you Warren ! * ******************************************************************************* */
/*********************
* CONSTANTS *********************/
// if you have a 16x2 standard LCD, untag this line : //#define LCD16x2 // otherwise, you are supposed to have the nice 20x4 LCD
// colors of pseudo-graphic screen
// pseudo-graphic function prototypes void miniGLCD_fill(unsigned char c) ; void miniGLCD_setPixel(char x, char y, unsigned char mode) ; void miniGLCD_line(char x0, char y0, char x1, char y1, unsigned char pcolor) ; unsigned char miniGLCD_getPixel(char x, char y) ;
/***************
* RAM VARIABLES ***************/
// horizontal frequency structure struct TIMEBASE
{ unsigned char t0con ; // timer 0 configuration unsigned char period[8] ; // period value unsigned char unit ; // period unit } timeBase[] = { { 0b10000100, "1.04857", ' ' }, { 0b10000011, "524.288", 'm' }, { 0b10000010, "262.144", 'm' }, { 0b10000001, "131.072", 'm' }, { 0b10000000, "65.536 ", 'm' }, { 0b11000111, "32.768 ", 'm' }, { 0b11000110, "16.384 ", 'm' }, { 0b11000101, "8.192 ", 'm' }, { 0b11000100, "4.096 ", 'm' }, { 0b11000011, "2.048 ", 'm' }, { 0b11000010, "1.024 ", 'm' }, { 0b11000001, "512 ", '\xe4' }, { 0b11000000, "256 ", '\xe4' }, { 0b11001000, "128 ", '\xe4' } } ;
unsigned char tbase = 0 ; // current timebase index
// vertical input range structure struct INPUT
{ unsigned char div ; // power of 2 of input divider unsigned char *ampl ; // range value in volts } input[] = { { 4, "2.500" }, { 2, "1.250" }, { 1, "0.625" }, } ;
unsigned char ipt = 0 ; // current input range index unsigned char vdiv ; // current power of 2 of input divider
// scrolling message unsigned char msg[][8] =
{ " ", " ", " ", " ", " LCD ", " SCOPE ", " By ", "BrunoG ", "* ", " ", " ", " see ", " more ", "details", " and ", "dwnload", "mikroC ", "source ", "code on", " ", "www. ", "micro- ", "example", "s.com ", "* ", " ", " ", "20x4LCD", "Black/ ", "Yellow ", "BkLight", "specs.&", "price :", " ", "www. ", "circuit", "-ed.com", "* ", " ", " ", " Thank ", " you ", "Warren!", "" } ;
unsigned char firstMsg = 0 ; // scrolling message index
unsigned char miniGLCD_screen[C_X * C_Y * 8] ; // pseudo-screen bitmap
unsigned char samples[miniGLCD_x] ; // sample table unsigned char sIdx = 0 ; // sample index
unsigned char trigger = 0 ; // trigger status unsigned char trigValue = 0 ; // trigger value
unsigned char hold = 0 ; // hold screen flag
unsigned int t0ctr ; // timer 0 overflow counter
/****************************
* INTERRUPT ROUTINE ****************************/
void interrupt()
{ // only timer 0 overflow is due to service if(INTCON.TMR0IF) { if(sIdx < miniGLCD_x) // is sampling in progress ? { if(trigger == 2) // is sampling triggered ? { // read ADC sample, adjust to range and store to sample buffer samples[sIdx++] = miniGLCD_y - (ADRESH >> vdiv) ; } else if(trigger == 1) // maximum was detected { // is signal rising down ? if((trigValue > LEVEL) && (ADRESH < trigValue - LEVEL)) { // yes, triggers sampling trigger = 2 ; } else { // no, update maximum value if(ADRESH > trigValue) { trigValue = ADRESH ; } } } else // looking for maximum { // is signal rising up ? if((trigValue < 255 - LEVEL) && (ADRESH > trigValue + LEVEL)) { // yes, next step is to wait for signal rising down trigger = 1 ; trigValue = 0 ; } else { // no, update minimum value if(ADRESH < trigValue) { trigValue = ADRESH ; } } } // start ADC, no sampling is required since ADC // is always connected to the same input ADCON0.GO = 1 ; }
t0ctr++ ; INTCON.TMR0IF = 0 ; } }
/*******************************
* UPDATE SETTINGS & DRAW SCREEN *******************************/
void mkScreen()
{ T0CON = timeBase[tbase].t0con ; // new timer 0 settings vdiv = input[ipt].div ; // store input divider
// ADC settings ADCON1 = 0b00001110 ; ADCON0 = 0b11000001 ;
LCD_out(1, 14, timeBase[tbase].period) ; // display period value LCD_chr(2, 15, timeBase[tbase].unit) ; // display period unit
LCD_out(3, 16, input[ipt].ampl) ; // display input range
// hold screen ? LCD_out(1, 9, hold ? "Hold" : "\xff\xff\xff\xff") ;
LCD_out(1, 12, timeBase[tbase].period) ; // display period value LCD_chr(2, 12, timeBase[tbase].unit) ; // display period unit
LCD_out(1, 1, input[ipt].ampl) ; // display input range
// hold screen ? LCD_out(1, 6, hold ? "H" : "\xff") ;
}
/************************
* UPDATE SCREEN AND DEBOUNCE PORTB KEYS ************************/
void debounce()
{ mkScreen() ; Delay_ms(20) ; while(PORTB != 0xff) ; Delay_ms(20) ; }
/***********************
* PSEUDO GRAPHIC FUNCTIONS ***********************/
/************************************************
* miniGLCD_fill : fill graphic screen with pattern * parameters : * c : filling pattern * for example : 0 for black screen, 0xff for white screen * returns : * nothing * requires : * miniGLCD_init must have been called * notes : * none */
void miniGLCD_fill(unsigned char c)
{ memset(miniGLCD_screen, c, sizeof(miniGLCD_screen)) ; }
/********************************************
* miniGLCD_setPixel : write pixel * parameters : * x : pixel row * y : pixel column * mode : miniGLCD_COLOR_WHITE or miniGLCD_COLOR_BLACK or miniGLCD_COLOR_REVERSE * returns : * nothing * requires : * miniGLCD_init must have been called * notes : * none */
void miniGLCD_setPixel(char x, char y, unsigned char mode)
{ unsigned char *ptr ; unsigned char mask ;
/* * do nothing if pixel is out of bounds */ if(x < 0) return ; if(y < 0) return ; if(x > miniGLCD_x) return ; if(y > miniGLCD_y) return ;
ptr = miniGLCD_screen + (((y * (C_X * 8)) + x) / 8) ; // points to byte in screen map mask = 1 << (x & 7) ; // pixel bit mask
switch(mode) { case miniGLCD_COLOR_BLACK: *ptr &= ~mask ; // clear bit break ; case miniGLCD_COLOR_WHITE: // set bit *ptr |= mask ; break ; default: *ptr ^= mask ; // toggle bit break ; } }
/********************************************
* miniGLCD_setPixel : read pixel * parameters : * x : pixel row * y : pixel column * returns : * color of pixel at (x, y) * requires : * miniGLCD_init must have been called * notes : * none */
unsigned char miniGLCD_getPixel(char x, char y)
{ unsigned char *ptr ; unsigned char mask ;
/* * do nothing if pixel is out of bounds */ if(x < 0) return(0) ; if(y < 0) return(0) ; if(x > miniGLCD_x) return(0) ; if(y > miniGLCD_y) return(0) ;
ptr = miniGLCD_screen + (((y * (C_X * 8)) + x) / 8) ; // points to byte in screen map mask = 1 << (x & 7) ; // pixel bit mask
return(*ptr & mask) ; }
/******************************
* miniGLCD_line : draw a line * parameters : * x0, y0 : pixel start coordinates * x1, y1 : pixel end coordinates * pcolor : miniGLCD_COLOR_WHITE or miniGLCD_COLOR_BLACK or miniGLCD_COLOR_REVERSE * returns : * nothing * requires : * miniGLCD_init must have been called * notes : * uses Bresenham's line drawing algorithm */
void miniGLCD_line(char x0, char y0, char x1, char y1, unsigned char pcolor)
{ int dy ; int dx ; int stepx, stepy ;
dy = y1 - y0 ; dx = x1 - x0 ;
if(dy < 0) { dy = -dy ; stepy = -1 ; } else { stepy = 1 ; }
if(dx < 0) { dx = -dx ; stepx = -1 ; } else { stepx = 1 ; }
dy <<= 1 ; dx <<= 1 ;
miniGLCD_setPixel(x0, y0, pcolor) ;
if(dx > dy) { int fraction = dy - (dx >> 1) ;
while(x0 != x1) { if(fraction >= 0) { y0 += stepy ; fraction -= dx ; } x0 += stepx ; fraction += dy ; miniGLCD_setPixel(x0, y0, pcolor) ; } } else { int fraction = dx - (dy >> 1) ;
while(y0 != y1) { if(fraction >= 0) { x0 += stepx ; fraction -= dy ; } y0 += stepy ; fraction += dx ; miniGLCD_setPixel(x0, y0, pcolor) ; } } }
/*************************************************************
* program custom character n at line pos_row column pos_char * if mode is not zero, also write custom char to LCD */
void CustomChar(unsigned char mode, unsigned char n, char pos_row, char pos_char)
{ unsigned char i, j ;
LCD_Cmd(64 + n * 8) ; for(i = 0 ; i < 8 ; i++) { unsigned char bm = 0 ;
for(j = 0 ; j < 5 ; j++) { bm <<= 1 ; bm |= miniGLCD_getPixel(pos_char * 5 + j, pos_row * 8 + i) ? 1 : 0 ; } LCD_Chr_Cp(bm) ; } LCD_Cmd(LCD_RETURN_HOME) ;
if(mode) LCD_Chr(pos_row + 2, pos_char + 9, n) ;
if(mode) LCD_Chr(pos_row + 1, pos_char + 7, n) ;
}
/******************
* MAIN LOOP ******************/
void main()
{ unsigned char i, j ; unsigned int wait ;
TRISA = 0xff ; // set PORTA as inputs
TRISB = 0xff ; // set PORTB as inputs INTCON2.NOT_RBPU = 0 ; // enables PORTB weak pull-ups TRISD = 0 ; // PORTD is output (LCD in 4bit mode)
// enables timer 0 overflow interrupt INTCON.TMR0IF = 0 ; INTCON.TMR0IE = 1 ; INTCON.GIE = 1 ;
// LCD configuration LCD_Init(&LATD) ; // Initialize LCD connected to PORTD LCD_Cmd(Lcd_CLEAR) ; // Clear display LCD_Cmd(Lcd_CURSOR_OFF) ; // Turn cursor off // display layout
LCD_out(1, 8, "\xff\xff\xff\xff\xff\xff") ; LCD_out(2, 8, "\xff \xff s/Div") ; LCD_out(3, 8, "\xff \xff") ; LCD_out(4, 8, "\xff\xff\xff\xff\xff\xff V/Div") ;
LCD_out(1, 1, " \xff \xff") ; LCD_out(2, 1, "V/Div\xff \xff s/Div") ;
// send custom chars CustomChar(1, 0, 0, 0) ; CustomChar(1, 1, 0, 1) ; CustomChar(1, 2, 0, 2) ; CustomChar(1, 3, 0, 3) ; CustomChar(1, 4, 1, 0) ; CustomChar(1, 5, 1, 1) ; CustomChar(1, 6, 1, 2) ; CustomChar(1, 7, 1, 3) ;
mkScreen() ;
for(;;) // forever { // if not in hold mode and samples buffer is full if((hold == 0) && (sIdx == miniGLCD_x)) { // clear pseudo-screen miniGLCD_fill(0) ;
// draw wave for(i = 0 ; i < miniGLCD_x - 1 ; i++) { j = i + 1 ;
miniGLCD_line(i, samples[i], j, samples[j], miniGLCD_COLOR_WHITE) ; }
// program custom chars CustomChar(0, 0, 0, 0) ; CustomChar(0, 1, 0, 1) ; CustomChar(0, 2, 0, 2) ; CustomChar(0, 3, 0, 3) ; CustomChar(0, 4, 1, 0) ; CustomChar(0, 5, 1, 1) ; CustomChar(0, 6, 1, 2) ; CustomChar(0, 7, 1, 3) ;
// restart trigger and samples index trigValue = 255 ; trigger = 0 ; sIdx = 0 ; }
// change horizontal frequency if(PORTB.F0 == 0) { tbase++ ; if(tbase == sizeof(timeBase) / sizeof(struct TIMEBASE)) { tbase = 0 ; } hold = 0 ; debounce() ; } else if(PORTB.F1 == 0) { if(tbase == 0) { tbase = sizeof(timeBase) / sizeof(struct TIMEBASE) - 1 ; } else { tbase-- ; } hold = 0 ; debounce() ; }
// change vertical range else if(PORTB.F2 == 0) { ipt++ ; if(ipt == sizeof(input) / sizeof(struct INPUT)) { ipt = 0 ; } hold = 0 ; debounce() ; } else if(PORTB.F3 == 0) { if(ipt == 0) { ipt = sizeof(input) / sizeof(struct INPUT) - 1 ; } else { ipt-- ; } hold = 0 ; debounce() ; }
// hold/release screen else if(PORTB.F4 == 0) { hold ^= 1 ; debounce() ; }
// scrolling message
if(wait) { if(t0ctr > (1u << (tbase + 5))) { firstMsg++ ; if(msg[firstMsg][0] == 0) { firstMsg = 0 ; }
t0ctr = 0 ; wait = 0 ; } } else if(t0ctr > (1u << (tbase + 1))) { j = firstMsg ; for(i = 1 ; i <= 4 ; i++) { if((i == 4) && (msg[j + 1][0] == '*')) { wait++ ; } if(msg[j][0] == '*') { LCD_out(i, 1, " ") ; } else { LCD_out(i, 1, msg[j]) ; }
j++ ; if(msg[j][0] == 0) { j = 0 ; } }
firstMsg++ ; if(msg[firstMsg][0] == 0) { firstMsg = 0 ; }
t0ctr = 0 ; }
} }
</pre>
Download LCDscope-project.ZIP file for mikroC : File:LCDscope-project.zip
Includes :