Using the Analog in and LCD, turn the MBED into a simple Oscilloscope

Dependencies:   C12832_lcd DebounceIn mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 //************************************************************************************
00002 // Main.cpp - main entry point and code for MBED based Oscilloscope.  
00003 //
00004 //  Author: Terry Richards
00005 //  Date:   June 10, 2014
00006 //
00007 #include "mbed.h"
00008 #include "C12832_lcd.h"
00009 #include "DebounceIn.h"
00010 #include "osc.h"
00011 
00012 Serial pc(USBTX, USBRX);//debug output
00013 C12832_LCD LCD;         //main LCD class
00014 AnalogIn Ain(p17);      //main analog input
00015 AnalogIn Aintrig(p18);  //External trigger input
00016 
00017 //AnalogIn pot1(p19);
00018 AnalogIn pot2(p20);     //sets trigger level
00019 DebounceIn Update(p14); //menu selection
00020 DebounceIn Up(p15);     //menu selection and vert base set
00021 DebounceIn Down(p12);   //menu selection and vert base set
00022 DebounceIn Left(p13);   //horiz (time) base set
00023 DebounceIn Right(p16);  //horiz (time) base set
00024 
00025 //Repaint the LCD with the menu strings for the 
00026 //current menu.  
00027 //Inputs - An array of menustrings and the first string
00028 //         to put on the display
00029 //Outputs - a freshly painted display with the menu
00030 #define FIRST_TEXTLINE      3   //Positions on the LCD
00031 #define SECOND_TEXTLINE     13
00032 #define THIRD_TEXTLINE      23
00033 int rePaint(char menustrings[][MENU_LENGTH], int first)
00034 {
00035     LCD.cls();
00036     LCD.locate( 0, FIRST_TEXTLINE );
00037     LCD.printf(menustrings[first]);
00038     LCD.locate( 0, SECOND_TEXTLINE );
00039     LCD.printf(menustrings[first+1]);
00040     LCD.locate( 0, THIRD_TEXTLINE );
00041     LCD.printf(menustrings[first+2]);
00042     return 0;
00043 }
00044 // Display a menu and allow scrolling until user
00045 // makes a selection.  Show the current selection
00046 // with a row of asterisks (which scroll with the
00047 // up and down buttons).
00048 // Inputs - The current selection and window min
00049 //          and max and strings for the current menu.
00050 //          These will change depending on the menu
00051 //          being displayed.  The sel_start is where
00052 //          the selection asterisks start for the 
00053 //          current display stings.
00054 // Outputs - A selection from the user from the current
00055 //           menu.
00056 //
00057 #define CLEARTORUN          6
00058 #define SETTORUN            4
00059 int display_menu(   int *cur_sel, 
00060                     int *cur_min, 
00061                     int *cur_max, 
00062                     char menustrings[][MENU_LENGTH], 
00063                     int sel_start, 
00064                     int menu_end )
00065 {
00066    while( 1 ) {
00067         if( Down ) {
00068              if( *cur_sel != menu_end ) {
00069                 memset( &menustrings[*cur_sel][sel_start-1], ' ' , CLEARTORUN);
00070                 *cur_sel += 1;
00071                 memset( &menustrings[*cur_sel][sel_start], '*', SETTORUN);
00072                 if( *cur_sel > *cur_max ) {
00073                     *cur_max += 1;
00074                     *cur_min += 1;
00075                     rePaint(menustrings, *cur_min);
00076                 }
00077                 else
00078                     rePaint(menustrings, *cur_min);
00079             }
00080             while( Down )
00081                 wait_ms(50);
00082         }
00083         if( Up ) {
00084             if( *cur_sel != 0 ) {
00085                 memset( &menustrings[*cur_sel][sel_start-1], ' ', CLEARTORUN);
00086                 *cur_sel -= 1;
00087                 memset( &menustrings[*cur_sel][sel_start], '*', SETTORUN);
00088                 if( *cur_sel < *cur_min ) {
00089                     *cur_max -= 1;
00090                     *cur_min -= 1;
00091                     rePaint(menustrings, *cur_min);
00092                 }
00093                 else
00094                     rePaint(menustrings, *cur_min);
00095             }
00096             while( Up )
00097                 wait_ms(50);    //Smooth things out
00098         }
00099         if( Update ) {  //When user presses center button, cur_sel has his selection
00100             return 0;
00101         }
00102     }
00103 }
00104 //Update the vertical and horizontal bases when the user presses the up or down joystick
00105 // buttons.  The horizontal is the time base, and the vertical is the volt base.
00106 // Inputs - none
00107 // Outputs - updated vertical and horizontal bases
00108 #define VBASEMAX        17
00109 #define TBASEMAX        9
00110 void Update_params(void)
00111 {   //divisions  1=128uS, 5=640uS, 10=1280uS, 20=2560uS, 50=6.4mS, 100=12.8mS, 500=64mS, 1000=128mS
00112     unsigned int next_tbase[] = { 1, 5, 10, 20, 50, 100, 500, 700, 1000 };
00113     float next_vbase[] = { 0.2, 0.4, 0.6, 0.8, 1.0, 1.4, 1.8, 2.0, 2.4, 2.8, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0 }; 
00114     static int vsel=4, tsel=0;
00115     if( Up ) {
00116         while( Up )
00117             wait_ms(1);
00118         vsel++;
00119         if( vsel == VBASEMAX )
00120             vsel = VBASEMAX-1;
00121         volt_base = next_vbase[vsel];
00122         }
00123     if( Down ) {
00124         while( Down )
00125             wait_ms(1);
00126         vsel--;
00127         if( vsel == -1 )
00128             vsel = 0;
00129         volt_base = next_vbase[vsel];
00130     }
00131     if( Right ) {
00132         while( Right )
00133             wait_ms(1);
00134         tsel++;
00135         if( tsel == TBASEMAX )
00136             tsel = TBASEMAX-1;
00137         time_base = next_tbase[tsel];
00138     }
00139     if( Left ) {
00140         while( Left )
00141             wait_ms(1);
00142         tsel--;
00143         if( tsel == -1 )
00144             tsel = 0;
00145         time_base = next_tbase[tsel];
00146     }
00147     tbsel = tsel;   //Global copy for menu
00148     //TODO: may need to add menu to update the trigger
00149     //level with the joystick.  Pot is a little twitchy
00150     trigger = pot2.read();  //set the trigger level
00151 }
00152 // Find an edge (either rising or falling based on trigger mode) and use
00153 // it to trigger a sweep of the scope display.  The search for an edge is
00154 // timed in case there is not a periodic waveform currently connected to
00155 // Ain.  This ensures the menu is not delayed too much.
00156 // Inputs - none
00157 // Outputs - time sync with a correct edge of the waveform
00158 //           or quick return if "no_trigger" is selected.
00159 //The trigger loops below take approx 20us per loop
00160 #define TRIG_TIMEOUT        1000    //1000*20us=20milliseconds
00161 bool get_trigger(void)
00162 {
00163     //add more timeout as the timebase increases
00164     //TODO: may need to be calibrated more fine grain at some point
00165     int timeout=TRIG_TIMEOUT + (tbsel*tbsel*TRIG_TIMEOUT);
00166     //capture a rising edge on the main signal - default mode
00167     if( trigger_mode == main_rising_edge ) {    
00168         while( (Ain.read() > trigger) && timeout )
00169             timeout--;
00170         if( !timeout )
00171             return false;
00172         else
00173             timeout = TRIG_TIMEOUT + (tbsel*TRIG_TIMEOUT);
00174         while( (Ain.read() < trigger) && timeout )
00175             timeout--;
00176         if( !timeout )
00177             return false;
00178         else
00179             return true;
00180     //capture a falling edge on the main signal
00181     } else if( trigger_mode == main_falling_edge ) {    
00182         while( (Ain.read() < trigger) && timeout )
00183             timeout--;
00184         if( !timeout )
00185             return false;
00186         else
00187             timeout = TRIG_TIMEOUT + (tbsel*TRIG_TIMEOUT);
00188         while( (Ain.read() > trigger) && timeout ) 
00189             timeout--;
00190         if( !timeout )
00191             return false;
00192         else
00193             return true;
00194     //capture a rising edge on an external signal
00195     } else if( trigger_mode == ext_rising_edge ) { 
00196         while( (Aintrig.read() > trigger) && timeout )
00197             timeout--;
00198         if( !timeout )
00199             return false;
00200         else
00201             timeout = TRIG_TIMEOUT + (tbsel*TRIG_TIMEOUT);
00202         while( (Aintrig.read() < trigger) && timeout )
00203             timeout--;
00204         if( !timeout )
00205             return false;
00206         else
00207             return true;
00208     //capture a falling edge on an external signal
00209     } else if( trigger_mode == ext_falling_edge ) {
00210         while( (Aintrig.read() < trigger) && timeout )
00211             timeout--;
00212         if( !timeout )
00213             return false;
00214         else
00215             timeout = TRIG_TIMEOUT + (tbsel*TRIG_TIMEOUT);
00216         while( (Aintrig.read() > trigger) && timeout )
00217             timeout--;
00218         if( !timeout )
00219             return false;
00220         else
00221             return true;
00222     } else if( trigger_mode == auto_no_trigger )
00223         //if auto no-trigger...do nothing. just return
00224         return true;
00225     return false;   //Can't get here, but silence the warning   
00226 }
00227 
00228 //This routine only gets called if the "draw grid" mode is selected. The
00229 // default is "no grid" so the display is a continuous display with no
00230 // lines.  The grid is more useful for some waveforms than others.  It is
00231 // a matter of taste as to if the user wants to display with or without grid.
00232 // Inputs - none
00233 // Outputs - the display with grid lines painted.
00234 #define NUMHORIZPIXELS      128
00235 #define NUMVERTPIXELS       32
00236 #define FIRSTVERT           21
00237 #define SECONDVERT          42
00238 #define THIRDVERT           63
00239 #define FOURTHVERT          84
00240 #define FIFTHVERT           105
00241 #define FIRSTHORIZ          11
00242 #define SECONDHORIZ         22
00243 void place_grid(void)
00244 {
00245     LCD.line(FIRSTVERT, 0, FIRSTVERT, NUMVERTPIXELS-1, 1);
00246     LCD.line(SECONDVERT, 0, SECONDVERT, NUMVERTPIXELS-1, 1);
00247     LCD.line(THIRDVERT, 0, THIRDVERT, NUMVERTPIXELS-1, 1);
00248     LCD.line(FOURTHVERT, 0, FOURTHVERT, NUMVERTPIXELS-1, 1);
00249     LCD.line(FIFTHVERT, 0, FIFTHVERT, NUMVERTPIXELS-1, 1);
00250     LCD.line(0, FIRSTHORIZ, NUMHORIZPIXELS-1, FIRSTHORIZ, 1);
00251     LCD.line(0, SECONDHORIZ, NUMHORIZPIXELS-1, SECONDHORIZ, 1);
00252 }
00253 
00254 void menu(void) 
00255 {
00256     int timeout=100;
00257     //Corrects the faster time bases for loop delay
00258     //TODO: These need to be further calibrated.  Maybe re-architected.
00259     unsigned int tbase_correction[] = { 24, 6, 3, 3, 1, 1, 1, 1, 1 };
00260     //While user has center button pressed, display the settings
00261     while( Update ) {
00262         LCD.cls();
00263         LCD.locate( 0, FIRST_TEXTLINE );
00264         LCD.printf("Vertical Base: %3.1f V/div", draw_grid? volt_base/3: volt_base );
00265         LCD.locate( 0, SECOND_TEXTLINE );
00266         LCD.printf("Horiz Base: %d uS/div", draw_grid? (((time_base*tbase_correction[tbsel])*128)/6): ((time_base*24)*128));
00267         LCD.locate( 0, THIRD_TEXTLINE );
00268         switch( trigger_mode ) {
00269             case main_rising_edge:
00270                 LCD.printf("Trigger Mode: Main rising");
00271                 break;
00272             case main_falling_edge:
00273                 LCD.printf("Trigger Mode: Main falling");
00274                 break;
00275             case ext_rising_edge:
00276                 LCD.printf("Trigger Mode: Ext rising");
00277                 break;
00278             case ext_falling_edge:
00279                 LCD.printf("Trigger Mode: Ext falling");
00280                 break;
00281             case auto_no_trigger:
00282                 LCD.printf("Trigger Mode: No Trigger");
00283         }
00284         wait_ms(200);
00285     }
00286     //Wait a while in case user presses center again
00287     while( !Update ) {
00288         --timeout;
00289         if( !timeout ) 
00290             break;  //If we hit time out, break out
00291         wait_ms(10);
00292     }
00293     if( timeout ) {     //If we didn't time out...continue
00294         timeout = 100;  //If we did..we just exit and go back to scope
00295         //User may be hitting center again, if so display tigger menu
00296         if( Update ) {  //user hit update again
00297             while( Update )
00298                 wait_ms(1);     //ensure we only process on hit
00299             rePaint(trigstrings, trig_cur_min);
00300             //Get a new trigger mode
00301             display_menu(&trig_cur_sel, &trig_cur_min, &trig_cur_max,
00302                 trigstrings, SELECTION_START, MAX_TRIG_MENU ); 
00303             trigger_mode = (trigger_mode_type)trig_cur_sel;
00304         }
00305         while( !Update ) {  //See if user wants to update grid settings
00306             --timeout;
00307             if( !timeout )
00308                 break;  //If we hit time out, break out
00309             wait_ms(10);
00310         }
00311         if( timeout ) {     //If we didn't time out..continue
00312             if( Update ) {  //user hit update again
00313                 while( Update )
00314                     wait_ms(1);     //ensure we only process one hit
00315                 rePaint(gridstrings, grid_cur_min);
00316                 //Get new grid settings
00317                 display_menu(&grid_cur_sel, &grid_cur_min, &grid_cur_max,
00318                     gridstrings, SELECTION_START, MAX_GRID_MENU );
00319                 if( grid_cur_sel )
00320                     draw_grid = true;
00321                 else
00322                     draw_grid = false;
00323             }
00324             while( Update )     //trickle out last switch hits
00325                 wait_ms(1);
00326         }
00327     }
00328 }
00329     
00330 //Main entry point and control loop for the program.  All routines
00331 // are called from here (and return here).
00332 //
00333 int main()
00334 {
00335     pc.baud(115200);    //debug output
00336     bool skip_first_trig = false;
00337     unsigned int i;
00338     unsigned int h = LCD.height()-2, w = LCD.width(), hhh;
00339     while(true) {       // thread loop
00340         if( get_trigger() || skip_first_trig ) {    //go get a trigger
00341             LCD.cls();
00342             LCD.rect(0,0,NUMHORIZPIXELS-1, NUMVERTPIXELS-1, 1); //place boarder
00343             if(draw_grid)
00344                 place_grid();           
00345             for (i=0; i<w; i++) {   //do a sweep
00346                 //scale the incoming signal to the screen size and vertical
00347                 //base setting.
00348                 hhh = (int)((h)-(h*volt_base*Ain.read()));
00349                 //does not paint the screen
00350                 LCD.pixel(i, hhh ,1);           // print pixel
00351                 //implements the horizontal or time-base
00352                 wait_us(time_base);
00353             }
00354             //paint the screen
00355             LCD.copy_to_lcd();  // LCD.pixel does not update the lcd 
00356         }
00357         skip_first_trig = false;
00358         Update_params();    //see if we changed time or volt bases
00359         if( Update ) {  //Hit the center button once to display stats
00360             skip_first_trig = true; //ensure quick screen update after menu
00361             menu();     //go get user input..Now on HID time
00362         } else
00363             wait_ms(50);    //Delay to smooth twitchy waveforms
00364     }
00365 }
00366 
00367 #if 0
00368 Testing:
00369 1. You need another MBED with a function generator running, or a suitable waveform generator (suitable in
00370 that it must generate waveforms in the frequency band of 1Hz to 2kHz, and amplitudes between 0 and 3.3 Volts).
00371 2. Waveforms greater than 2kHz are displayable on the MBED Oscilloscope, but are not very interesting.  They
00372 are two fast for the MBED processor to keep up with.  The Analog Input takes 20 uS to get a reading so it is
00373 limited in the frequencies it can handle.
00374 3. See the writeup on the MBED site for instructons for the menuing system that sets the scope parameters.
00375 4. Connect the output of the other MBED or the function generator to pin 17 of the MBED running the Osc 
00376 software, or use a "scope probe" connected to the Ain 1/8 phone jack on the side of the MBED running the Osc
00377 software.  Note that the other jack (Aout) can be used as an external trigger if desired, however the two jacks are
00378 so close together on the application board that you will need to find very small 1/8 phone jacks to fit two
00379 together side-by-side.
00380 5. "Scope probes" can be constructed by obtaining an audio cable (stereo or mono will work), cut it in half so 
00381 you have two cables with bare wires on one end and an 1/8 phone jack on the other.  Use a DMM to determine the 
00382 bare wires that are connected to the p17 and GND on the MBED (you can do this most easily by plugging the cable
00383 into the Ain jack and connecting one probe of the DMM to p17 and the other to the bare wires and see which one
00384 has continuity.  Then do the same with GND).  Now solder a clip to both the p17 wire and the GND wire so you 
00385 can connect these to your device-under-test.
00386 #endif