Using the Analog in and LCD, turn the MBED into a simple Oscilloscope
Dependencies: C12832_lcd DebounceIn mbed
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
Generated on Tue Jul 12 2022 15:21:35 by 1.7.2