www.micro-examples.com : LCDscope

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

How to build a simple and low-cost oscilloscope with a text LCD ?

Contents

PicoDetector : a PIC-based simple and cheap metal detector

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.

Circuit Schematic

LCDscope-schematic.png

  • Coil L1 replaces crystal in PIC oscillator circuit : try with the coils you have to find the best one ! if calibrate LED blinks on power up, it works.
  • D1 is a dual LED, replace with two classic LEDs if you don't have one.

C Source code

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

  1. ifndef LCD16x2
  2. define LCD20x4
  3. endif
  1. define C_X 4 // number of columns in pseudo-graphic screen
  2. define C_Y 2 // number of rows in pseudo-graphic screen
  1. define miniGLCD_x (5*C_X) // number of X pixels in pseudo-graphic screen
  2. define miniGLCD_y (8*C_Y) // number of Y pixels in pseudo-graphic screen
  1. define LEVEL 16 // trigger sensitivity (number of ADC points)

// colors of pseudo-graphic screen

  1. define miniGLCD_COLOR_WHITE 0
  2. define miniGLCD_COLOR_BLACK 1
  3. define miniGLCD_COLOR_REVERSE 2

// 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

  1. ifdef LCD20x4

// 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

  1. endif

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 ;
  1. ifdef LCD20x4
       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") ;
  1. else
       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") ;
  1. endif
       }

/************************

* 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) ;
  1. ifdef LCD20x4
       if(mode) LCD_Chr(pos_row + 2, pos_char + 9, n) ;
  1. else
       if(mode) LCD_Chr(pos_row + 1, pos_char + 7, n) ;
  1. endif
       }

/******************

* 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
  1. ifdef LCD20x4
       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") ;
  1. else
       LCD_out(1, 1, "     \xff    \xff") ;
       LCD_out(2, 1, "V/Div\xff    \xff s/Div") ;
  1. endif
       // 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
  1. ifdef LCD20x4
               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 ;
                       }
  1. endif
               }
       }

</pre>

Download project

Download LCDscope-project.ZIP file for mikroC : File:LCDscope-project.zip

Includes :

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

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