├── README.md ├── Leonardo CNC Speed Control SchematicR2.pdf ├── CNCSpindleSpeedControl ├── LCDKeypadR1.h ├── LCDKeypadR1.cpp ├── keywords.txt └── CNCSpindleSpeedControl.ino └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-CNC-Speed-Control 2 | Arduino/Leonardo Speed Control Code; As shown on YouTube Video http://youtu.be/kYFNUQgE26E 3 | -------------------------------------------------------------------------------- /Leonardo CNC Speed Control SchematicR2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmharvey1/Arduino-CNC-Speed-Control/HEAD/Leonardo CNC Speed Control SchematicR2.pdf -------------------------------------------------------------------------------- /CNCSpindleSpeedControl/LCDKeypadR1.h: -------------------------------------------------------------------------------- 1 | /* 2 | LCDKeypad.h 3 | */ 4 | 5 | // ensure this library description is only included once 6 | #ifndef LCDKeypad_h 7 | #define LCDKeypad_h 8 | 9 | // library interface description 10 | #define KEYPAD_NONE -1 11 | #define KEYPAD_RIGHT 0 12 | #define KEYPAD_UP 1 13 | #define KEYPAD_DOWN 2 14 | #define KEYPAD_LEFT 3 15 | #define KEYPAD_SELECT 4 16 | 17 | class LCDKeypad: public LiquidCrystal 18 | { 19 | public: 20 | LCDKeypad(); 21 | int button(); 22 | }; 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /CNCSpindleSpeedControl/LCDKeypadR1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | LCDKeypad.cpp 3 | */ 4 | 5 | #include "Arduino.h"//#include "WProgram.h" 6 | 7 | // include this library's description file 8 | 9 | #include 10 | #include "LCDKeypadR1.h" 11 | 12 | LCDKeypad::LCDKeypad() : LiquidCrystal(8, 9, 4, 5, 6, 7) 13 | { 14 | } 15 | 16 | int LCDKeypad::button() 17 | { 18 | static int NUM_KEYS=5; 19 | static int adc_key_val[5] ={ 20 | 30, 150, 360, 535, 760 }; 21 | int k, input; 22 | input=analogRead(0); 23 | for (k = 0; k < NUM_KEYS; k++) 24 | { 25 | if (input < adc_key_val[k]) 26 | { 27 | return k; 28 | } 29 | } 30 | if (k >= NUM_KEYS) 31 | k = -1; // No valid key pressed 32 | return k; 33 | } 34 | -------------------------------------------------------------------------------- /CNCSpindleSpeedControl/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Test 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | LCDKeypad KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | button KEYWORD2 16 | 17 | ####################################### 18 | # Instances (KEYWORD2) 19 | ####################################### 20 | 21 | ####################################### 22 | # Constants (LITERAL1) 23 | ####################################### 24 | KEYPAD_NONE LITERAL1 25 | KEYPAD_RIGHT LITERAL1 26 | KEYPAD_UP LITERAL1 27 | KEYPAD_DOWN LITERAL1 28 | KEYPAD_LEFT LITERAL1 29 | KEYPAD_SELECT LITERAL1 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 jmharvey1 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /CNCSpindleSpeedControl/CNCSpindleSpeedControl.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This Program when uploaded to a Arduino/Leonardo (or Uno) is a Tach based speed controller. 3 | It was originally written to operate a 400W/48VDC CNC spindle motor through 4 | a simple electronic controller, readily found on E-Bay 5 | This program assumes that the +PWM lead of the controller is connected to 6 | pin D13 of the Leonardo (or Pin D11 on the UNO), and the collector output of the speed sensor 7 | [OPB704 is a direct pin-for-pin replacement for the once popular, 8 | but now discontinued, QRB1114; the Parallax Inc 550-27401 or FairChild qrd1114 9 | might also maybe adapted to work] is connected to pin D3. 10 | Note this collector is also tied +5Volts through a 20K pull-up resistor. 11 | It Furhter assumes that a Sansmart 16x2 LCD/KeyPad sheild has been installed. 12 | This display & keypad provides a simple user interface, where the Up/Down buttons 13 | can be used to ramp the Set Speed from 1400 to 10,000 RPM, in steps of 200 RPM, 14 | while the Left/Right Buttons will select the display mode. 15 | 16 | This version of the speed control sketch incudes a "Scope" display mode. This mode 17 | is new to this release. To access the Scope mode, press either the "Left" or "Right" 18 | buttons on the display's key pad. Hint: using the "Left" button, it will be the first key press. 19 | The purpose of this mode is to allow the user, to use the 16 x 2 display as a rudimentary 20 | Oscilloscope. The Arduino's "A1" pin acts as the Scope's input. Typically either the IR's Output signal 21 | which is normally connected to the Arduino's interrupt pin (D3) can be also connected to A1. 22 | This gives the user a practical way to observe the spindle's speed signal. And through this view, 23 | the user can adjust the IR sensor's position for maximum effectiveness. 24 | The ideal pulse should look something like what is shown here: 25 | /--------------------\ /----- 26 | / \-/ 27 | It may be helpful to make the initial position adjustments with the IR's output only connected to 28 | A1 (interrupt pin D3 not connected). With this arrangement the spindle should run at a steady 29 | speed regardless of what the sensor is outputing, and will allow the user to explore a range of positions 30 | without the spindle speed going crazy. Once the best spot it found, add the interrupt connection, and 31 | then the user can "fine tune" this position by continuing to monitor the speed pulse on A1. 32 | 33 | Note: The LCDKeypadR1 Library, contained in this file set, 34 | is a derivative of the files found here: 35 | 36 | http://sainsmart.com/zen/documents/20-011-901/keypad_lcd.zip 37 | 38 | To install this sketch, when down loaded from GitHub all the files found under the sub-directory 39 | "CNCSpindleSpeedControl" should be left in that directory. and the entire directory 40 | [CNCSpindleSpeedControl] moved to the same directory as the Uer's other sketches 41 | On a windows based computer, this is usually the /Documents/Arduino/ directory. 42 | If setup correctly, when you open the sketch, you should see three tabs. 43 | "CNCSpindleSpeedControl", "LCDKeypadR1.cpp", and "LCDKeypadR1.h" 44 | 45 | 46 | Permission is hereby granted, free of charge, to any person obtaining a copy 47 | of this software and associated documentation files (the "Software"), to deal 48 | in the Software without restriction, including without limitation the rights 49 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 50 | copies of the Software, and to permit persons to whom the Software is 51 | furnished to do so, subject to the following conditions: 52 | 53 | The above copyright notice and this permission notice shall be included in 54 | all copies or substantial portions of the Software. 55 | 56 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 57 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 58 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 59 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 60 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 61 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 62 | THE SOFTWARE. 63 | =============================================== 64 | */ 65 | #include 66 | #include "LCDKeypadR1.h" 67 | 68 | #if defined(__AVR_ATmega32U4__) 69 | //Code in here will only be compiled if an Arduino Leonardo is used. 70 | #define TimerReg TCCR4B 71 | #define BoardType 0 72 | #define InterruptId 0 73 | #endif 74 | #if defined(__AVR_ATmega16U4__) 75 | //Code in here will only be compiled if an Arduino Uno is used. 76 | #define TimerReg TCCR2B 77 | #define BoardType 1 78 | #define InterruptId 1 79 | #endif 80 | 81 | #if defined(__AVR_ATmega328P__) 82 | //Code in here will only be compiled if an Arduino Uno is used. 83 | #define TimerReg TCCR2B 84 | #define BoardType 1 85 | #define Interrupt 1 86 | #define InterruptId 1 87 | #endif 88 | 89 | LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //initialize Sansmart LCD Display 90 | LCDKeypad Keypad; 91 | int trgtRPM = 5000; 92 | unsigned long LstIntTime = 0; 93 | unsigned long Start = 0; 94 | unsigned long Stop; 95 | unsigned long WaitInterval =0; 96 | unsigned long startPrgm = millis()/1000; 97 | unsigned long now; 98 | unsigned long QrtrSecTimeOut = millis() ; 99 | int SampleCnt = 10; // Number Spindle Relolutions needed to make a new RPM calculation 100 | unsigned long period; 101 | unsigned long Maxperiod; 102 | 103 | int UsrInput = 0; 104 | int PWMpin; // Digital Pin that PWM signal appears on (Pin 13 = Leonardo; Pin 11 = Uno) 105 | int calcrpm = 0; 106 | int sensorPin = A1; // Used for Oscope Mode (mode 3) 107 | int IRAnalogBuf[80];// Buffer/Array to hold Scaled analog data Used for Oscope Mode (mode 3) 108 | // -----------------PID/PWM Constants ----------------- 109 | unsigned long lastTime; 110 | double Input, SetPoint; 111 | double ITerm, lastInput; 112 | double kp, ki, kd; 113 | double Kp = 0.02;//0.06; 114 | double Ki = 0.05;//0.05; 115 | double Kd = 0.0005;//0.0005; 116 | double PIDOut = 0.0; 117 | double Max = 254.0; //max allowed PWM value 118 | double Min = 1.0; //min allowed PWM value 119 | int SampleTime = 60;// measured in milliseconds [FWIW: 10,000 RPM measured over 10 revolutions = 60 ms] 120 | int MstrSampleTime = SampleTime; 121 | 122 | // -----------------End PID Constants ----------------- 123 | long int TotalRunTime = 0; 124 | int MaxRunTime = 0;//60*60*10;// number of seconds the program is allowed to run be 125 | int SpndlPWM; 126 | int newdutycycle = 0; 127 | int dutycycle = 30; // percentage of time clock is high 128 | //int StrtDC = 0; // the calculated Start dutycycle needed to hit the target rpm $ 129 | 130 | //int LastRPM = 0; 131 | //int AltRPM = 0; 132 | //int SpeedChange = 0; 133 | //int SuspectRdCnt = 0; 134 | //bool increaseSpeed = true; 135 | bool NewTrgtVal = true; //used to determine if display needs updating 136 | bool ExitNow = false; 137 | bool NuInput = true; 138 | bool ActivIntrpt = false; 139 | bool chkBtns = true; 140 | //char *intFlgStat; 141 | //int ThrtlLpCnt= 0; 142 | //int Startup = 0; // Locks out pid calcs on startup to give the default throttle setting a chance to take effect before correcting it 143 | //int CalcIntrvl = 0; 144 | int BackLight = 6; // set Display BackGround color to 1=Red; 2=Green; 4=Blue 145 | int Mode = 7; 146 | 147 | // the event counter 148 | volatile int eventCounter = 0; 149 | //volatile int LoopCounter = 0; 150 | 151 | char buf [16]; // used to concatenate strings & numerical data for display on SanSmart LCD 152 | 153 | // Display Custom characters for Throttle Bar Graph Mode 154 | static unsigned char Char0 [8] = 155 | { 156 | 0b00000, 157 | 0b00000, 158 | 0b00000, 159 | 0b00000, 160 | 0b00000, 161 | 0b00000, 162 | 0b00000, 163 | 0b00000 164 | } ; 165 | 166 | static unsigned char Char1 [8] = 167 | { 168 | 0b10000, 169 | 0b10000, 170 | 0b10000, 171 | 0b10000, 172 | 0b10000, 173 | 0b10000, 174 | 0b10000, 175 | 0b10000 176 | } ; 177 | 178 | static unsigned char Char2 [8] = 179 | { 180 | 0b11000, 181 | 0b11000, 182 | 0b11000, 183 | 0b11000, 184 | 0b11000, 185 | 0b11000, 186 | 0b11000, 187 | 0b11000 188 | } ; 189 | 190 | static unsigned char Char3 [8] = 191 | { 192 | 0b11100, 193 | 0b11100, 194 | 0b11100, 195 | 0b11100, 196 | 0b11100, 197 | 0b11100, 198 | 0b11100, 199 | 0b11100 200 | } ; 201 | 202 | static unsigned char Char4 [8] = 203 | { 204 | 0b11110, 205 | 0b11110, 206 | 0b11110, 207 | 0b11110, 208 | 0b11110, 209 | 0b11110, 210 | 0b11110, 211 | 0b11110, 212 | } ; 213 | 214 | static unsigned char Char5 [8] = 215 | { 216 | 0b11111, 217 | 0b11111, 218 | 0b11111, 219 | 0b11111, 220 | 0b11111, 221 | 0b11111, 222 | 0b11111, 223 | 0b11111 224 | } ; 225 | 226 | 227 | // O-Scope bitmap 228 | static unsigned char Char0101 [16]; 229 | static unsigned char Char0102 [16]; 230 | static unsigned char Char0103 [16]; 231 | static unsigned char Char0104 [16]; 232 | static unsigned char Char0105 [16]; 233 | static unsigned char Char0106 [16]; 234 | static unsigned char Char0107 [16]; 235 | static unsigned char Char0108 [16]; 236 | static unsigned char Char0109 [16]; 237 | static unsigned char Char0110 [16]; 238 | static unsigned char Char0111 [16]; 239 | static unsigned char Char0112 [16]; 240 | static unsigned char Char0113 [16]; 241 | static unsigned char Char0114 [16]; 242 | static unsigned char Char0115 [16]; 243 | static unsigned char Char0116 [16]; 244 | static unsigned char Trace1 [8]; 245 | static unsigned char Trace2 [8]; 246 | static unsigned char Trace3 [8]; 247 | static unsigned char Trace4 [8]; 248 | static unsigned char Trace5 [8]; 249 | static unsigned char Trace6 [8]; 250 | static unsigned char Trace7 [8]; 251 | static unsigned char Trace8 [8]; 252 | static unsigned char TstBtMap [8]; 253 | 254 | 255 | // ================================================================ 256 | // === INTERRUPT DETECTION ROUTINE === 257 | // ================================================================ 258 | 259 | // SpindleTachInterrupt: called every time an event occurs 260 | void SpindleTachInterrupt(void) 261 | { 262 | // detachInterrupt(InterruptId); 263 | unsigned long ThisIntTime = micros(); 264 | LstIntTime = millis(); 265 | if (ThisIntTime < WaitInterval) return; 266 | WaitInterval = ThisIntTime+3000; //move the next valid interrupt out by 3 milliSeconds (the squivalent of the spindle turning @ 20K RPM ) 267 | if (eventCounter == 1) 268 | { 269 | Start = ThisIntTime; 270 | ActivIntrpt = true; 271 | } 272 | if (eventCounter == SampleCnt+1) 273 | { 274 | period = ThisIntTime-Start; 275 | } 276 | eventCounter +=1; 277 | if (eventCounter >= SampleCnt+2) 278 | { 279 | //ActivIntrpt = false; 280 | if (period ==0) sprintf (buf,"Too Small"); 281 | else 282 | { 283 | //int Freq = (SampleCnt*1000000)/period; //Simple Frequency Counter (Hz) code 284 | //sprintf (buf,"Hz: %d ", Freq); //Simple Frequency Counter code 285 | calcrpm = (SampleCnt*60000000)/period; 286 | if (calcrpm <=2800.0) SampleCnt = 2; 287 | else SampleCnt = 10; 288 | 289 | } 290 | SampleTime = MstrSampleTime; 291 | SetTunings(Kp, Ki, Kd); 292 | SpndlPWM = CalcMtrPID(calcrpm);//Max+CalcMtrPID(calcrpm); 293 | analogWrite(PWMpin, SpndlPWM); 294 | dutycycle = (int)((SpndlPWM*100)/(Max));//(int)((SpndlPWM*100)/(2*Max));// convert PWM signal to a precentage 295 | // LoopCounter = 1;//reset loopcounter so main loop will know that Motor is still running 296 | eventCounter = 0; 297 | } 298 | // delay(3); //wait at least the equivalent of 20K rpm before reacting to another spindle interrupt 299 | // attachInterrupt(InterruptId, SpindleTachInterrupt, FALLING); 300 | } 301 | 302 | // ================================================================ 303 | // === END INTERRUPT DETECTION ROUTINE === 304 | // ================================================================ 305 | 306 | // ------------------------------------------------------------------------- 307 | 308 | // ================================================================ 309 | // === INITIAL SETUP === 310 | // ================================================================ 311 | 312 | void setup() 313 | { 314 | byte Divisor; 315 | //Serial1.begin(9600); // enable when [Leonardo] bluetooth diagnostic is needed 316 | Serial.begin(9600); // enable when/if diagnostic is needed 317 | // set up the lcd's number of columns and rows: 318 | lcd.begin(16, 2); 319 | //calcrpm = (SampleCnt*1000000*60)/period;//Formula used in interrupt routine to estimate current Spindle RMPM 320 | calcrpm = 500; //minimum expected RPM 321 | Maxperiod =(SampleCnt*1000000*60)/calcrpm; 322 | 323 | pinMode(3, INPUT_PULLUP);//Set Interrupt input pin to have an active pullupresistor 324 | if (BoardType == 0) // Leonardo 325 | { 326 | PWMpin = 13; 327 | Divisor = 0x06;//0x06 original divisor used on Leonardo; new faster pwm divisor 0x04 328 | //Serial.println("Leonardo"); 329 | } 330 | else //Uno 331 | { 332 | PWMpin = 11; 333 | Divisor = 0x03; 334 | //Serial.println("Uno"); 335 | } 336 | attachInterrupt(InterruptId, SpindleTachInterrupt, FALLING); //UNO digital pin 3; 337 | 338 | TimerReg = TimerReg & 0b11111000 | Divisor; 339 | pinMode(PWMpin, OUTPUT); 340 | 341 | //lcd.setCursor(0,0); 342 | //lcd.print("SetUp Complete"); 343 | //startPrgm = 1000*millis(); 344 | long MaxRunTime = 60*60*10;// number of seconds the program is allowed to run be 345 | } 346 | 347 | 348 | // ================================================================ 349 | // === MAIN PROGRAM LOOP === 350 | // ================================================================ 351 | void loop() 352 | { 353 | 354 | int stopCnt = 0; 355 | bool UpdtDis = false; 356 | chkBtns = true; 357 | unsigned long CurPrdTime; 358 | unsigned long lstDspTime =millis(); 359 | lcd.setCursor(0,1); 360 | lcd.print("Speed Cntl Ready"); 361 | lcd.setCursor(0,0); 362 | sprintf (buf,"Set RPM: %d", trgtRPM); 363 | lcd.print(buf); 364 | //If you're here [and can see, "Speed Cntl Ready" on the Display] 365 | //then LCD setup seemed to go OK, & you're ready to start the speed control stuff 366 | 367 | // Calc initial dutycycle based on trgtRPM 368 | // SpndlPWM = (int) 95.0*(trgtRPM/9800.0)*(trgtRPM/9800.0) ; //190.5*(trgtRPM/9800.0)*(trgtRPM/9800.0) 369 | 370 | // display counter value every 1/4 second. 371 | //sprintf (buf, "Time %d Max %d\n", TotalRunTime, MaxRunTime); 372 | //Serial1.println(buf); 373 | while ( TotalRunTime <= MaxRunTime && !ExitNow ) 374 | { 375 | CurPrdTime = millis()-LstIntTime; 376 | //Serial.println(CurPrdTime); 377 | if(CurPrdTime >= Maxperiod/1000 ){// if true, it looks like the motor isn't turning 378 | 379 | eventCounter = 0; 380 | // LoopCounter = 0; 381 | LstIntTime = millis(); 382 | NewTrgtVal = true; 383 | ActivIntrpt = false; 384 | } 385 | //while (eventCounter == 0 && LoopCounter == 0 && TotalRunTime <= MaxRunTime && !ExitNow) 386 | while (!ActivIntrpt && TotalRunTime <= MaxRunTime && !ExitNow) 387 | { 388 | //Motor is not running, So initialize PID for next startup 389 | //SpndlPWM = (int) 190.5*(trgtRPM/9800.0)*(trgtRPM/9800.0) ; 390 | ResetSpindle(); 391 | SpndlPWM = 15; 392 | //sprintf (buf, "PWMpin:%02i PWM:%02i", PWMpin, SpndlPWM); 393 | //Serial.println(buf); 394 | //delay(100); 395 | ScanButtons(); 396 | 397 | if (NewTrgtVal) 398 | { 399 | lcd.setCursor(0,0); //lcd.setCursor(pos,line) 400 | sprintf (buf, "%s %04i ", "Trgt RPM:", trgtRPM); 401 | lcd.print(buf); 402 | lcd.setCursor(0,1); 403 | lcd.print("Speed Cntl Ready"); 404 | NewTrgtVal = false; 405 | } 406 | if (Mode == 3){// Run Scope Mode Routine 407 | ScopeMode(); 408 | } 409 | long TotalRunTime = calcruntime(); 410 | //SuspectRdCnt = 3; 411 | stopCnt = stopCnt+1; 412 | } // end of 2nd inner While looop 413 | // Serial1.print( "rpm: %d; trgtRPM: %d; Duty Cycle: %d; SpdChng: %d %s; EvntCnt: %d \n", calcrpm, trgtRPM,$ 414 | if (NewTrgtVal) 415 | { 416 | lcd.setCursor(0,0); //lcd.setCursor(pos,line) 417 | sprintf (buf, "%s %04i ", "Trgt RPM:", trgtRPM); 418 | lcd.print(buf); 419 | NewTrgtVal = false; 420 | } 421 | if((millis()-lstDspTime)>500){ 422 | UpdtDis= true; 423 | lstDspTime =millis(); 424 | } 425 | else UpdtDis= false; 426 | if (UpdtDis){ 427 | //Serial.println(millis()-lstDspTime); 428 | lcd.setCursor(0,1);//lcdPosition (lcdHandle, 0, 1) ; 429 | if (Mode == 7) // Original Display Mode; Show Measured RPM, on the 2nd line 430 | { 431 | //lcdPrintf (lcdHandle, "Actl RPM: %d ", calcrpm) ; 432 | sprintf (buf, "%s %04i ", "Actl RPM:", calcrpm); 433 | lcd.print(buf); 434 | } 435 | if (Mode == 5) // Display Duty Cycle Mode 436 | { 437 | char DsplyStr[16]; 438 | char BlnkChr [1]; 439 | sprintf(DsplyStr, "Duty Cycle: %d ", dutycycle );// convert SpdEr to a String 440 | strncpy( BlnkChr, " ", 1);// now fill out the rest of the display message/line with Blanks 441 | //[to ensure previous message has been erased 442 | while (strlen(DsplyStr)<16) 443 | { 444 | strcat(DsplyStr, BlnkChr); 445 | } 446 | //lcdPrintf (lcdHandle,"%s", DsplyStr); 447 | //sprintf (buf, "%s %04i ", "Actl RPM:", calcrpm); 448 | lcd.print(DsplyStr); 449 | } 450 | if (Mode == 6) // show Speed error Mode 451 | { 452 | char SpdErStr[6]; 453 | char SgndErStr[8]; 454 | char *pntr; 455 | int SpdEr = calcrpm-trgtRPM; 456 | sprintf(SpdErStr, "%d",SpdEr );// convert SpdEr to a String 457 | pntr = strchr (SpdErStr, '-'); 458 | if(pntr == NULL) 459 | { 460 | strncpy(SgndErStr, " +", 8); 461 | strcat(SgndErStr, SpdErStr); 462 | } 463 | else 464 | { 465 | strncpy(SgndErStr, " ", 8); 466 | strcat(SgndErStr, SpdErStr); 467 | } 468 | strncpy( SpdErStr, " ", 6); 469 | while (strlen(SgndErStr)<8) 470 | { 471 | strcat(SgndErStr, SpdErStr); 472 | } 473 | //lcdPrintf (lcdHandle, "Spd Err:%s", SgndErStr ) ; 474 | sprintf (buf, "%s%s ", "Spd Err:", SgndErStr); 475 | lcd.print(buf); 476 | } 477 | 478 | } 479 | // ############ Begin BarGraph Mode ################# 480 | if (Mode == 4)// PWM Bar Graph Mode 481 | { 482 | int FullCell; 483 | int runngCnt = 0; 484 | int postn = 0; 485 | int remainder = 0; 486 | int rowWeight; 487 | lcd.setCursor(0,1);//lcdPosition (lcdHandle, 0, 1) ; 488 | FullCell = 100/16;//(Maxdutycycle - Mindutycycle)/16; 489 | rowWeight = FullCell/5; 490 | // First, fill in the whole spaces 491 | while (runngCnt + FullCell<= dutycycle) 492 | { 493 | //lcdPutchar (lcdHandle, 7) ; 494 | lcd.write(byte(5)); 495 | runngCnt = runngCnt + FullCell; 496 | postn = postn + 1; 497 | } 498 | // Next, Deterime which partial charater to use 499 | remainder = dutycycle- runngCnt; 500 | //Serial1.print("remainder: %d Dutycycle: %d\n", remainder, dutycycle-Mindutycycle); 501 | if(remainder>0) 502 | { 503 | postn = postn + 1; 504 | if (remainder>= 4*rowWeight) 505 | { 506 | lcd.write(byte(4));//lcdPutchar (lcdHandle, 6) ; 507 | } 508 | else 509 | { 510 | if (remainder>= 3*rowWeight) 511 | { 512 | lcd.write(byte(3));//lcdPutchar (lcdHandle, 5) ; 513 | } 514 | else 515 | { 516 | if (remainder>= 2*rowWeight) 517 | { 518 | lcd.write(byte(2));//lcdPutchar (lcdHandle, 4) ; 519 | } 520 | else 521 | { 522 | lcd.write(byte(1));//lcdPutchar (lcdHandle, 3) ; 523 | } 524 | } 525 | } 526 | } 527 | 528 | // finally, Finish out the Bar Graph line with blanks 529 | while (postn < 16) 530 | { 531 | lcd.write(byte(0));//lcdPutchar (lcdHandle, 2) ; 532 | postn = postn + 1; 533 | } 534 | } 535 | // ############ End BarGraph Mode ################# 536 | if (Mode == 3){// Run Scope Mode Routine 537 | ScopeMode(); 538 | } 539 | // ############ End O-Scope Mode ################# 540 | 541 | 542 | ScanButtons(); 543 | // if (UpdtDis){ 544 | // LoopCounter++; 545 | // } 546 | long TotalRunTime = calcruntime(); 547 | } //end of 1st while loop 548 | //kill the PWM signal 549 | detachInterrupt(InterruptId); 550 | digitalWrite (PWMpin, LOW); 551 | lcd.clear(); 552 | lcd.setCursor(0,0); //lcd.setCursor(pos,line) 553 | sprintf (buf, "Program Stopped"); 554 | lcd.print(buf); 555 | lcd.setCursor(0,1); //lcd.setCursor(pos,line) 556 | sprintf (buf, "CYCLE PWR 2 STRT"); 557 | lcd.print(buf); 558 | delay(10000); 559 | return; 560 | } 561 | // ================================================================ 562 | // === END MAIN PROGRAM LOOP === 563 | // ================================================================ 564 | 565 | //########################################################################### 566 | // Begin PID computations 567 | int CalcMtrPID(float Input) 568 | { 569 | unsigned long now = millis(); 570 | int timeChange = (now - lastTime); 571 | if(timeChange>=SampleTime) 572 | { 573 | //sprintf (buf, "DT %d", timeChange); 574 | //Serial1.println(buf); 575 | SetSampleTime(timeChange); 576 | /*Compute all the working error variables*/ 577 | if (SetPoint < trgtRPM) SetPoint +=200; 578 | else if(SetPoint > trgtRPM) SetPoint -=200; 579 | double error = SetPoint - Input; 580 | double dInput = (Input - lastInput); 581 | double offset = 0.0; 582 | double DtI = ki*error; 583 | double Cap = 10.0; 584 | if (DtI >Cap) DtI = Cap; 585 | else if (DtI <-Cap) DtI = -Cap; 586 | ITerm+= DtI;//ITerm+= (0.75*ki*error); 587 | 588 | /*Compute PID PIDOut*/ 589 | PIDOut = kp*error + ITerm - kd * dInput; 590 | //sprintf (buf, "P %d\n", PIDOut); 591 | //Serial1.println(PIDOut); 592 | 593 | SetPIDOutLimits(Min, Max); 594 | /*Remember some variables for next time*/ 595 | lastInput = Input; 596 | lastTime = now; 597 | } 598 | return (int) PIDOut; 599 | 600 | } 601 | 602 | void SetTunings(double Kp, double Ki, double Kd) 603 | { 604 | double SampleTimeInSec = ((double)SampleTime)/1000; 605 | kp = Kp; 606 | ki = Ki * SampleTimeInSec; 607 | kd = Kd / SampleTimeInSec; 608 | } 609 | 610 | void SetSampleTime(int NewSampleTime) 611 | { 612 | if (NewSampleTime > 0) 613 | { 614 | double ratio = (double)NewSampleTime 615 | / (double)SampleTime; 616 | ki *= ratio; 617 | kd /= ratio; 618 | SampleTime = (unsigned long)NewSampleTime; 619 | } 620 | } 621 | 622 | void SetPIDOutLimits(double Min, double Max) 623 | { 624 | if(Min > Max) return; 625 | // double outMin = Min; 626 | // double outMax = Max; 627 | 628 | // if(PIDOut > outMax) PIDOut = outMax; 629 | // else if(PIDOut < outMin) PIDOut = outMin; 630 | // 631 | // if(ITerm> outMax) ITerm= outMax; 632 | // else if(ITerm< outMin) ITerm = outMin; 633 | 634 | if (PIDOut > Max) 635 | { 636 | ITerm -= PIDOut - Max; 637 | PIDOut = Max; 638 | } 639 | else if (PIDOut < Min) 640 | { 641 | ITerm -=(PIDOut - Min);//+= Min - PIDOut;// -=(PIDOut - Min) 642 | PIDOut = Min; 643 | } 644 | } 645 | 646 | 647 | // End PID computations 648 | //####################################################################### 649 | 650 | // ================================================================ 651 | // === BEGIN LOCAL FUNCTION DEFINITIONS === 652 | // ================================================================ 653 | 654 | 655 | void ScanButtons() 656 | { 657 | if(!chkBtns){ 658 | if (millis()-QrtrSecTimeOut >250){ 659 | QrtrSecTimeOut = millis(); 660 | //LoopCounter = LoopCounter + 1; 661 | chkBtns = !chkBtns; 662 | } 663 | else return; 664 | } 665 | int buttons = DecodeButton(); 666 | if (buttons == -1){ 667 | NuInput = true; 668 | return; 669 | } 670 | int NubtnVal = buttons; //remember previous button reading 671 | int i = 0; 672 | // Button debounce code to help ensure a true button reading 673 | while(i<200 ){ 674 | buttons = DecodeButton(); 675 | if (NubtnVal == buttons) i++; 676 | else {// doesn't match; Reset & Start over 677 | NubtnVal = buttons; 678 | i= 0; 679 | } 680 | } 681 | if (buttons > 0 ){ 682 | chkBtns = false;// lock out new button scan for another delay interval (original wait period was250 ms) 683 | QrtrSecTimeOut = millis(); // reset delay interval timer 684 | UpDateSettings(buttons, Mode); 685 | } 686 | 687 | return; 688 | } 689 | 690 | int DecodeButton(){ 691 | int buttons = 0; 692 | int buttonPressed = Keypad.button(); 693 | if (buttonPressed==KEYPAD_NONE)return -1; 694 | if (buttonPressed==KEYPAD_UP) buttons += 1; 695 | if (buttonPressed==KEYPAD_DOWN) buttons += 2; 696 | if (buttonPressed==KEYPAD_LEFT) buttons += 4; 697 | if (buttonPressed==KEYPAD_RIGHT) buttons += 8; 698 | if (buttonPressed==KEYPAD_SELECT) buttons += 16; 699 | return buttons; 700 | } 701 | 702 | void UpDateSettings(int UsrInput, int mode) 703 | { 704 | if(UsrInput == 1 || UsrInput == 2) //[Display Up or Down Button] Speed, $ 705 | { 706 | { 707 | NewTrgtVal = true; 708 | if(UsrInput == 1 ) 709 | { 710 | if (trgtRPM <10000) 711 | { 712 | trgtRPM = trgtRPM+200; 713 | } 714 | } 715 | else 716 | { 717 | if (trgtRPM >1500) 718 | { 719 | trgtRPM = trgtRPM-200; 720 | } 721 | } 722 | } 723 | } 724 | if((UsrInput == 4 || UsrInput == 8) && NuInput) //[Display Left o$ 725 | { 726 | NuInput = false; 727 | // Serial1.print("NuInput %s\n", NuInput ? "true" : "false"); 728 | if (Mode == 3){ //user is leaving Scope mode so restore Display 729 | lcd.setCursor(0,0); 730 | lcd.clear(); 731 | sprintf (buf,"Trgt RPM: %d", trgtRPM); 732 | lcd.print(buf); 733 | } 734 | if (UsrInput == 4) 735 | { 736 | Mode = Mode +1; 737 | if (Mode == 8) 738 | { 739 | Mode = 3; 740 | //Mode = 4; 741 | } 742 | } 743 | else 744 | { 745 | Mode = Mode- 1; 746 | if (Mode == 2) 747 | { 748 | Mode = 7; 749 | } 750 | 751 | } 752 | //setBacklightColour (BackLight); 753 | if (Mode == 4) LoadBarGraphFont();//user just selected bargraph mode; load custom characters to support 754 | } 755 | if (UsrInput ==16) 756 | { 757 | ExitNow = true; 758 | } 759 | } 760 | 761 | 762 | // ------------------------------------------------------------------------- 763 | // Function that, given a start time, will calculate an interger number of seconds 764 | // that the progam has been running 765 | 766 | long int calcruntime() 767 | { 768 | now = millis()/1000;//gettimeofday(&now, NULL); 769 | long int RunTime = (now -startPrgm); //TotalRunTime = ((now.tv_sec - startPrgm.tv_sec)); 770 | return RunTime; 771 | } 772 | // ------------------------------------------------------------------------- 773 | void ResetSpindle(){ 774 | SpndlPWM = 15; 775 | SetPoint = 2000; 776 | ITerm = 0.0; //kill any residual Integral component that might be left over from a previous run 777 | lastTime = millis();// reset/establish time mark for initial PID calculation 778 | attachInterrupt(InterruptId, SpindleTachInterrupt, FALLING); //UNO digital pin 3; 779 | analogWrite(PWMpin, SpndlPWM); //start the PWM output 780 | } 781 | // ------------------------------------------------------------------------- 782 | 783 | void LoadBarGraphFont(){ 784 | // install user defined special characters 785 | lcd.createChar(0, Char0); 786 | // create a new character 787 | lcd.createChar(1, Char1); 788 | // create a new character 789 | lcd.createChar(2, Char2); 790 | // create a new character 791 | lcd.createChar(3, Char3); 792 | // create a new character 793 | lcd.createChar(4,Char4); 794 | // create a new character 795 | lcd.createChar(5, Char5); 796 | } 797 | // ------------------------------------------------------------------------- 798 | void ScopeMode(){ 799 | int AsmplCnt =0 ; 800 | int sensorValue; 801 | int RowWeight = 64; 802 | bool KeepWaiting = true; 803 | unsigned long LoopTime; 804 | unsigned long GoNow = micros(); 805 | AsmplCnt =0; 806 | int uSWait = (int)((period/(SampleCnt*10))-900)/8; // returns the number of micro seconds to wait/sample to collect 80 data points given the current spindle speed 807 | // Serial.println("Mode3"); 808 | if (!ActivIntrpt) KeepWaiting = !KeepWaiting; 809 | while(eventCounter == 0 & KeepWaiting){ 810 | LoopTime = micros()-GoNow; 811 | if (LoopTime > 300000){ 812 | ActivIntrpt = false; 813 | KeepWaiting = false; 814 | } 815 | } 816 | while (eventCounter != 0 & KeepWaiting){ 817 | 818 | LoopTime = micros()-GoNow; 819 | if (LoopTime > 300000){ 820 | ActivIntrpt = false; 821 | KeepWaiting = false; 822 | } 823 | } 824 | if(KeepWaiting){ // if true, we are receiving interrupts; So use the interrupt method to establish sample frequency 825 | GoNow = micros(); 826 | unsigned long pause = (period/(SampleCnt))/2; 827 | while (KeepWaiting){ 828 | LoopTime = micros()-GoNow; 829 | if (LoopTime > pause){ 830 | KeepWaiting = false; 831 | } 832 | } 833 | //Serial.println("Interrupt Trigger"); 834 | while (AsmplCnt<80){ //while (eventCounter < 2 & AsmplCnt<80){ 835 | sensorValue = 1024-analogRead(sensorPin); 836 | int Drow = 0; 837 | int RowVal=0; 838 | while (RowVal< sensorValue){ 839 | RowVal = RowVal+RowWeight; 840 | Drow++; 841 | } 842 | IRAnalogBuf[AsmplCnt] = Drow; 843 | AsmplCnt++; 844 | GoNow = micros()+uSWait;//150;// 845 | while (micros()< GoNow); 846 | 847 | } 848 | } 849 | else{ // no interrupts detected, try to look for a dip in the analog sense reading to sync/start sample timing 850 | //Serial.println("Slope Trigger"); 851 | ResetSpindle(); 852 | GoNow = micros(); 853 | uSWait = 90; 854 | LoopTime = 000; 855 | while(analogRead(sensorPin)< 800 & LoopTime<60000){ // wait long enough for a motor turning at at least 1,000 rpm to generate a dip in the analog reading 856 | LoopTime = micros()-GoNow; 857 | } 858 | while(analogRead(sensorPin)> 600 & LoopTime<60000){ // wait long enough for a motor turning at at least 1,000 rpm to generate a dip in the analog reading 859 | LoopTime = micros()-GoNow; 860 | } 861 | while (AsmplCnt<80){ 862 | sensorValue = 1024-analogRead(sensorPin); 863 | int Drow = 0; 864 | int RowVal=0; 865 | while (RowVal< sensorValue){ 866 | RowVal = RowVal+RowWeight; 867 | Drow++; 868 | } 869 | IRAnalogBuf[AsmplCnt] = Drow; 870 | AsmplCnt++; 871 | GoNow = micros()+uSWait;//150;// 872 | while (micros()< GoNow); 873 | 874 | } 875 | 876 | } 877 | // sprintf (buf, "uSwait:%d ", uSWait); 878 | // Serial.println(buf); 879 | IRAnalogBuf[AsmplCnt] =-1; 880 | MapBuf2BitMap(); 881 | LoadTrace(); 882 | // for( int i=0; i< 16; i++){ 883 | // sprintf (buf, "%d,",Char0101[i]); 884 | // Serial.println(buf); 885 | // } 886 | 887 | 888 | // for( int i=0; i< 80; i++){ 889 | // sprintf (buf, "%d, ",IRAnalogBuf[i]); 890 | // Serial.print(buf); 891 | // //Serial.print(", "); 892 | // } 893 | // Serial.println(""); 894 | //sprintf (buf, "%s%d ", "Period:", uSWait); 895 | // sprintf (buf, "%s%d ", "Smpl Cnt:", AsmplCnt); 896 | // lcd.print(buf); 897 | 898 | 899 | }// End Scope Mode Function 900 | // ------------------------------------------------------------------------- 901 | 902 | void MapBuf2BitMap(){// takes the 80 analog samples just collected & creates 16 5x16 bitmaps [16 x 5= 80] 903 | int ChrPos = 1; 904 | unsigned char *BktPtr; 905 | unsigned char BitMask = 0b10000; 906 | ResetArrays();// clears out old data such that each of the 16 buckets have 5 bit zereos in each of their 16 rows 907 | BktPtr =Rtn5x16BktPtr(ChrPos); 908 | for (int i = 0; IRAnalogBuf[i] != -1; i++){ 909 | // sprintf(buf,"%d, %d\n\r",IRAnalogBuf[i]-1,BitMask); 910 | // Serial.print(buf); 911 | *(BktPtr+(IRAnalogBuf[i]-1))= *(BktPtr+(IRAnalogBuf[i]-1)) | BitMask; 912 | //*(BktPtr+2)= *(BktPtr+2) | BitMask; // For testing synsthesize a straight trace 913 | BitMask = (unsigned int)BitMask>>1; 914 | if (BitMask == 0){ 915 | BitMask = 0b10000; 916 | ChrPos++; 917 | BktPtr =Rtn5x16BktPtr(ChrPos); 918 | // Serial.print(i); 919 | // Serial.print(", "); 920 | } 921 | }// end "for" loop 922 | } 923 | 924 | 925 | 926 | 927 | void ResetArrays(){ 928 | unsigned char *BktPtr; 929 | for(int DatAry = 1; DatAry < 17; DatAry++){ 930 | BktPtr =Rtn5x16BktPtr(DatAry); 931 | for (int i = 0 ; i < 16; i++){ 932 | *(BktPtr+i) = 0b00000; 933 | } 934 | } 935 | } 936 | 937 | 938 | void ResetCstmChars(){ 939 | unsigned char *BktPtr; 940 | for(int DataAry = 1; DataAry < 9; DataAry++){ 941 | BktPtr =RtnSpclChrPtr(DataAry); 942 | for (int i = 0 ; i < 8; i++){ 943 | *(BktPtr+i) = 0b11111; 944 | } 945 | } 946 | } 947 | 948 | void LoadTrace(){// Draw Oscilloscope Trace (Using Scaled Analog just collected) on 16x2 SanSmart Display 949 | int SpclChrCnt = 0; 950 | int Line1 [16]; // bucket to hold 1st line of 16 bitmaps to be displayed 951 | int Line2 [16]; // bucket to hold 2nd line of 16 bitmaps to be displayed 952 | unsigned char *TracePtr; // create another Pointer label 953 | unsigned char *TstPtr;// create another Pointer label 954 | ResetCstmChars(); 955 | // now sequence through the 5x16 bitmaps, & from the custom bitmap collection, assigm them as needed to each of the 16 Display grids/positions in line1 & line2 956 | unsigned char *BktPtr; 957 | for (int ColPos = 1 ; ColPos < 17; ColPos++){ 958 | BktPtr =Rtn5x16BktPtr(ColPos); 959 | // Now for the current character position, extract a 5x8 bitmap for the 1st line of the display 960 | // Then determine if it matches one of the exixting custom characters; If not, add it to the custom character collection (max of 8) 961 | for (int i = 0 ; i < 8; i++){ 962 | TstBtMap[i]= *(BktPtr+i); 963 | } 964 | // next check the existing special character bitmaps to see if this TstBtMap has a match 965 | TstPtr = &TstBtMap[0]; 966 | int MatchingChr = TstBtMp4Match(SpclChrCnt, TstPtr); 967 | if (MatchingChr != -1){ Line1[ColPos-1] = MatchingChr; 968 | // Serial.println("Yes"); 969 | } 970 | else {// no match; it's a new bitmap 971 | SpclChrCnt++; 972 | // sprintf (buf, "NO %d",SpclChrCnt); 973 | // Serial.println(buf); 974 | if(SpclChrCnt<9){// we haven't yet maxed out the number of custom characters we can define; So add this one to the collection 975 | TracePtr = RtnSpclChrPtr(SpclChrCnt);// get a pointer to the next available custom bitmap bucket 976 | for (int i = 0 ; i < 8; i++){// transfer the contents of this 5x8 bitmap to our newly select custom bitmap bucket 977 | *(TracePtr+i)= *(TstPtr+i); 978 | } 979 | Line1[ColPos-1] = SpclChrCnt-1; //update the line 1 bucket so that it can later tell the display where to find this custom bitmap 980 | } 981 | else {Line1[ColPos-1] = -1; 982 | // Serial.println("OverFlow"); 983 | } 984 | } 985 | // Now for the current character position, repeat the above process but use the lower 1/2 of the 5x16 bitmapbucket for the 2nd line of the display 986 | for (int i = 0 ; i < 8; i++){ 987 | TstBtMap[i]= *(BktPtr+(i+8)); 988 | } 989 | // now check the existing special character bitmaps to see if this TstBtMap for a match 990 | TstPtr = &TstBtMap[0]; 991 | MatchingChr = TstBtMp4Match(SpclChrCnt, TstPtr); 992 | if (MatchingChr != -1) Line2[ColPos-1] = MatchingChr; 993 | else { 994 | SpclChrCnt++; 995 | if(SpclChrCnt<9){ 996 | TracePtr = RtnSpclChrPtr(SpclChrCnt); 997 | for (int i = 0 ; i < 8; i++){ 998 | *(TracePtr+i)= *(TstPtr+i); 999 | } 1000 | Line2[ColPos-1] = SpclChrCnt-1; 1001 | } 1002 | else Line2[ColPos-1] = -1; 1003 | } 1004 | } //End for Loop 1005 | // now load this round's custom charater Set into the 16x2's user defined character ram/registers (max limit of 8) 1006 | for (int i = 0 ; i < SpclChrCnt & i < 8; i++){ 1007 | TracePtr = RtnSpclChrPtr(i+1); 1008 | lcd.createChar(i, TracePtr); 1009 | } 1010 | // now write out the character assignments to the first line 1011 | lcd.setCursor(0,0); //lcd.setCursor(pos,line) 1012 | for (int i = 0 ; i < 16; i++){ 1013 | if(Line1[i]!= -1) lcd.write(byte(Line1[i])); 1014 | else lcd.print(" ");//lcd.write(byte(3)); 1015 | // sprintf (buf, "%d, ",Line1[i]); 1016 | // Serial.print(buf); 1017 | } 1018 | // Serial.println(""); 1019 | // now write out the character assignments to the 2nd line 1020 | lcd.setCursor(0,1); //lcd.setCursor(pos,line) 1021 | for (int i = 0 ; i < 16; i++){ 1022 | if(Line2[i]!= -1 ) lcd.write(byte(Line2[i])); 1023 | else lcd.print(" ");//lcd.write(byte(3)); 1024 | // sprintf (buf, "%d, ",Line2[i]); 1025 | // Serial.print(buf); 1026 | } 1027 | // Serial.println(""); 1028 | }//end of LoadTrace() function 1029 | 1030 | 1031 | 1032 | int TstBtMp4Match(int SpclChrCnt, unsigned char *TstPtr){ 1033 | unsigned char *TracePtr; 1034 | bool IsSame = true; 1035 | int RtnPtr = -1; 1036 | if (SpclChrCnt == 0) return RtnPtr; 1037 | for (int CurSpclBtMp = 1; CurSpclBtMp < SpclChrCnt+1 & CurSpclBtMp < 9 ; CurSpclBtMp++){ 1038 | IsSame = true; 1039 | TracePtr = RtnSpclChrPtr(CurSpclBtMp); 1040 | for (int i = 0 ; i < 8; i++){ 1041 | if(*(TstPtr+i)!= *(TracePtr+i)) IsSame = false; 1042 | } 1043 | if (IsSame) return CurSpclBtMp-1; 1044 | } 1045 | return RtnPtr; 1046 | } 1047 | 1048 | 1049 | unsigned char* RtnSpclChrPtr(int SpclChrCnt){ // returns a pointer to the designated bitmap array 1050 | unsigned char *TracePtr; 1051 | switch (SpclChrCnt) { 1052 | case 1: 1053 | TracePtr = &Trace1[0];// assosciate the pointer to the Sat1 array 1054 | break; 1055 | case 2: 1056 | TracePtr = &Trace2[0];// assosciate the pointer to the Sat2 array 1057 | break; 1058 | case 3: 1059 | TracePtr = &Trace3[0];// assosciate the pointer to the Sat3 array 1060 | break; 1061 | case 4: 1062 | TracePtr = &Trace4[0];// assosciate the pointer to the Sat4 array 1063 | break; 1064 | case 5: 1065 | TracePtr = &Trace5[0];// assosciate the pointer to the Sat1 array 1066 | break; 1067 | case 6: 1068 | TracePtr = &Trace6[0];// assosciate the pointer to the Sat2 array 1069 | break; 1070 | case 7: 1071 | TracePtr = &Trace7[0];// assosciate the pointer to the Sat3 array 1072 | break; 1073 | case 8: 1074 | TracePtr = &Trace8[0];// assosciate the pointer to the Sat4 array 1075 | break; 1076 | } 1077 | return TracePtr; 1078 | } 1079 | 1080 | 1081 | 1082 | unsigned char* Rtn5x16BktPtr(int ChrPos){ // returns a pointer to the designated bitmap array 1083 | unsigned char *BktPtr; 1084 | switch (ChrPos) { 1085 | case 1: 1086 | BktPtr = &Char0101[0];// assosciate the pointer to the Sat1 array 1087 | break; 1088 | case 2: 1089 | BktPtr = &Char0102[0];// assosciate the pointer to the Sat2 array 1090 | break; 1091 | case 3: 1092 | BktPtr = &Char0103[0];// assosciate the pointer to the Sat3 array 1093 | break; 1094 | case 4: 1095 | BktPtr = &Char0104[0];// assosciate the pointer to the Sat4 array 1096 | break; 1097 | case 5: 1098 | BktPtr = &Char0105[0];// assosciate the pointer to the Sat1 array 1099 | break; 1100 | case 6: 1101 | BktPtr = &Char0106[0];// assosciate the pointer to the Sat2 array 1102 | break; 1103 | case 7: 1104 | BktPtr = &Char0107[0];// assosciate the pointer to the Sat3 array 1105 | break; 1106 | case 8: 1107 | BktPtr = &Char0108[0];// assosciate the pointer to the Sat4 array 1108 | break; 1109 | case 9: 1110 | BktPtr = &Char0109[0];// assosciate the pointer to the Sat1 array 1111 | break; 1112 | case 10: 1113 | BktPtr = &Char0110[0];// assosciate the pointer to the Sat2 array 1114 | break; 1115 | case 11: 1116 | BktPtr = &Char0111[0];// assosciate the pointer to the Sat3 array 1117 | break; 1118 | case 12: 1119 | BktPtr = &Char0112[0];// assosciate the pointer to the Sat4 array 1120 | break; 1121 | case 13: 1122 | BktPtr = &Char0113[0];// assosciate the pointer to the Sat1 array 1123 | break; 1124 | case 14: 1125 | BktPtr = &Char0114[0];// assosciate the pointer to the Sat2 array 1126 | break; 1127 | case 15: 1128 | BktPtr = &Char0115[0];// assosciate the pointer to the Sat3 array 1129 | break; 1130 | case 16: 1131 | BktPtr = &Char0116[0];// assosciate the pointer to the Sat4 array 1132 | break; 1133 | } 1134 | return BktPtr; 1135 | } 1136 | 1137 | 1138 | 1139 | 1140 | --------------------------------------------------------------------------------