AtomicELS.ino
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- // Copyright (c) 2020 Jon Bryan
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in all
- // copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- // SOFTWARE.
- //================================================================================
- //
- // ELECTRONIC LEADSCREW
- // Jon R. Bryan
- // 2020
- //
- // This runs on an Arduino Mega (ATmega2560).
- //
- // The target lathe in this case is a 1946 10" Logan Model 200, Serial #38108
- //
- // The spindle encoder, which is mounted to the upper arm of the banjo, tucked in under
- // the reversing lever, and driven 1:1 by a modified 60-tooth timing pulley mounted
- // in place of the stud gear via a 6mm wide 220-2GT belt, outputs 800 counts per revolution.
- // The CL57Y stepper controller/driver from omc-stepperonline.com is set to 400 counts per
- // revolution, and controls a 3.5Nm Nema 24 motor mounted under the lower banjo arm.
- // The stepper drives the leadscrew through 8:1 (15t/120t) HTD3M timing pulleys and
- // a 16mm HTD420-3M belt (for 3200 steps per leadscrew revolution).
- // Everything fits (barely) with no permanent modifications to the lathe.
- // The leadscrew has a pitch of 8tpi (25600 steps per inch).
- // The carriage feeds at a rate of 39.0625 microinches per step.
- // It takes 256 steps to move the carriage or cross feed 0.010".
- //
- // Pin change interrupts are used for spindle quadrature decoding and counting.
- // A timer/counter in PWM mode outputs the appropriate number of leadscrew steps per spindle tick.
- // Leadscrew steps are counted by a timer/counter overflow interrupt.
- //
- // This will handle spindle speeds over 1500rpm up to one step for every spindle tick.
- // With 800-count spindle encoder resolution that is an interrupt every 50us (20khz).
- // The maximum rate that the stepper controller can handle is 200khz.
- // The absolute theoretical maximum would be 10 step pulses per spindle count,
- // which would translate to a nonsensical 0.250" per revolution feed at 1500rpm.
- // As implemented, the output period is adjusted and the maximum spindle RPM recalculated
- // for each feed rate to keep the maximum lead screw rate approximately constant
- // over the lathe's speed range, resulting in a speed limit of 70rpm at the maximum
- // rate of 4tpi (56rpm at 6.5mm).
- //
- // A "Nextion" color LCD touchscreen, a 20ppr encoder knob and a
- // MOM-OFF-MOM toggle switch provide the user interface.
- // A great deal of functionality is bound up in the Nextion
- // display programming, so refer to the Nextion configuration.
- // There is no error checking in the Nextion communications, so there's a bit of risk there.
- // The display is configured to return data only on failures.
- // No problem as long as there's never a glitch. An error-checking wrapper would be possible?
- //
- //================================================================================
- #include "configuration.h"
- #include "tables.h"
- //Fixed synchronization bug 04/21/2020:jrb
- //Added RPM blanking according to feed rate 04/22/2020:jrb
- //================================================================================
- // Setup
- //================================================================================
- void setup(void) {
- //Configure the input pins
- pinMode(SPINDLE_A, INPUT); //Spindle encoder quadrature inputs
- pinMode(SPINDLE_B, INPUT); //Put 2k pullups on these inputs to avoid spurious interrupts
- pinMode(ALARM, INPUT_PULLUP); //ALM+ open collector output from controller (not implemented yet)
- pinMode(LEFT_MOM, INPUT_PULLUP); //MOM-OFF-MOM toggle switch for controlling feed direction
- pinMode(RIGHT_MOM, INPUT_PULLUP);
- Serial.begin(38400); //Keep the default port for debugging
- Serial2.begin(38400); //Use USART2 for the Nextion display
- nextionInit(); //Initialize the Nextion display
- nextionLeft(); //Defaults to feeding toward the headstock
- feedSelect(inch_feed); //Default to INCH mode
- feedFill(inch[pitchFind(" 40")].steps); //Initialize the lookup table for 40tpi (1 step per spindle tick).
- tc3Init(); //Initialize the timers
- tc4Init();
- pcint4Enab(); //Enable timer interrupt
- tc3Enab(); //Enable timers
- tc4Enab();
- zeroSet(!waitSerial2); //Zero the leadscrew and clear the limits.
- //Falling edges are sharper, and generally provide better noise margin
- attachInterrupt(digitalPinToInterrupt(KNOB_A), knob, FALLING);
- attachInterrupt(digitalPinToInterrupt(SPINDLE_A), spindle, FALLING);
- steps = 0;
- }
- //================================================================================
- // Loop
- //================================================================================
- void loop(void) {
- int i;
- for (i = 0; i < 4; i++) {
- knobCheck(); //Has the encoder knob been turned?
- toggleCheck(); //Feed switch activated?
- nextionCheck(); //Has the display sent anything?
- nextionDirection(); //Correctly reflect the feed direction
- nextionLead(); //Display the leadscrew position.
- delay(25); //No point in updating too fast
- }
- nextionRPM(); //Display less often so the flicker isn't so distracting.
- }
- /**************************************************************
- ***************************************************************
- *** ***
- *** Functions that communicate with the Nextion display ***
- *** ***
- ***************************************************************
- **************************************************************/
- void nextionInit(void) {
- //Initialize the state of the display buttons, etc.
- //I dislike the "magic numbers" for the colors, but I'm not enough of a
- //C programmer to work around them without more effort than it seems to be worth here.
- //F() puts the string constants in flash memory, at least.
- //The display design is done in the Nextion editor.
- //Refer to the Nextion .HMI display configuration file for the object names and id's.
- Serial2.print(F("page start\xFF\xFF\xFF"));
- Serial2.print(F("inch_btn.pco=WHITE\xFF\xFF\xFF")); //INCH text white
- Serial2.print(F("metric_btn.pco=BLACK\xFF\xFF\xFF")); //METRIC black
- Serial2.print(F("diam_btn.pco=BLACK\xFF\xFF\xFF")); // etc...
- Serial2.print(F("module_btn.pco=BLACK\xFF\xFF\xFF"));
- Serial2.print(F("inch_btn.bco=1024\xFF\xFF\xFF")); //INCH background toggle
- Serial2.print(F("inch_btn.bco=44415\xFF\xFF\xFF")); //44415 = pale blue
- Serial2.print(F("shoulder.left_lim.txt=\"-----\"\xFF\xFF\xFF"));
- Serial2.print(F("shoulder.right_lim.txt=\"-----\"\xFF\xFF\xFF"));
- Serial2.print(F("shoulder.left_lim.bco=33816\xFF\xFF\xFF")); //33816 = dark blue
- Serial2.print(F("shoulder.right_lim.bco=33816\xFF\xFF\xFF")); //33816 = dark blue
- //Serial2.print(F("thup=1\xFF\xFF\xFF")); //Auto wake on touch
- //Serial2.print(F("thsp=600\xFF\xFF\xFF")); //Sleep on no touch after 10 minutes
- }
- void nextionRPM(void) {
- // Sends the spindle rpm value to the display.
- char str[30];
- static unsigned int spin;
- int rpm;
- //If stopped, spin_rate is set to 0xFFFF by TC3 overflow interrupt.
- if ((spin = spinAvg(spin_rate)) < RPM20) {
- if (fault) {
- //A fault means that an encoder interrupt occurred before all the steps were output,
- //indicating that the lathe is running too fast for the feed rate.
- //Make RPM background red. Might need more for a color-blind operator.
- sprintf(str, "%s%s", "rpm.bco=RED", NX_END);
- Serial2.print(str);
- }
- rpm = T3CPM / spin / SCPR;
- sprintf(str, "%s%d%s", "rpm.txt=\"", rpm, QTNX_END);
- } else {
- sprintf(str, "%s%s", "rpm.txt=\"0", QTNX_END);
- }
- Serial2.print(str);
- }
- void nextionLead(void) {
- //Output the leadscrew position to the display
- char leadstr[20];
- char str[50];
- sprintf(str, "%s%s%s", "shoulder.leadscrew.txt=\"", leadStr(leadscrew, leadstr), QTNX_END);
- Serial2.print(str);
- }
- void nextionCheck(void) {
- //Handler for touch events from the display
- char buf[20];
- //Things get a little complicated here.
- //Wait for an event from the Nextion display.
- //The mode buttons only send a "press" event.
- //The left/right and limit buttons also send a "release" event
- //which is used in the called functions to implement delays and spin checks.
- if (Serial2.available()) {
- Serial2.readBytes(buf, 7);
- //The mode-select ID's 1 through 4 appear on all three Nextion pages.
- //It took some care to make sure that the ID's were the same on each page.
- //ID's 5 and 6 are the left and right feed select "arrows", 7 is the "BACK" button,
- //and 8, 9 and 10 are the "LEFT", "ZERO" and "RIGHT" limit buttons.
- // touch page 0 page 1 page 2 press
- if (buf[0] == 0x65 && (buf[1] == 0x00 || buf[1] == 0x01 || buf[1] == 0x02) && buf[3] == 0x01) {
- //Figure out which button press came from the touchscreen:
- switch (buf[2]) { // Component ID
- case inch_btn:
- feed_mode = inch_feed;
- break;
- case metric_btn:
- feed_mode = metric_feed;
- break;
- case diametral_btn:
- feed_mode = diametral_feed;
- break;
- case module_btn:
- feed_mode = module_feed;
- break;
- case left_btn:
- //Left arrow
- jogCheck(true);
- break;
- case right_btn:
- //Right arrow
- jogCheck(false);
- break;
- case lset_btn:
- leftSet();
- break;
- case zset_btn:
- zeroSet(waitSerial2);
- break;
- case rset_btn:
- rightSet();
- break;
- case lclr_btn:
- leftClr(waitSerial2);
- break;
- case rclr_btn:
- rightClr(waitSerial2);
- break;
- }
- }
- feedSelect(feed_mode);
- }
- }
- void leftSet(void) {
- //Set the left limit and update the display
- char lead[10];
- char str[30];
- left_limited = true;
- left_limit = leadscrew;
- sprintf(str, "%s%s%s", "shoulder.left_lim.bco=", nxDGREEN, NX_END);
- Serial2.print(str);
- sprintf(str, "%s%s%s", "shoulder.left_lim.txt=\"", leadStr(left_limit, lead), QTNX_END);
- Serial2.print(str);
- }
- void rightSet(void) {
- //Set the right limit and update the display
- char lead[10];
- char str[30];
- right_limited = true;
- right_limit = leadscrew;
- sprintf(str, "%s%s%s", "shoulder.right_lim.bco=", nxDGREEN, NX_END);
- Serial2.print(str);
- sprintf(str, "%s%s%s", "shoulder.right_lim.txt=\"", leadStr(right_limit, lead), QTNX_END);
- Serial2.print(str);
- }
- void leftClr(bool wait) {
- //Clear the limit and update the display
- if (spin_rate != SPINDLE_STOPPED) {
- //Refuse if the spindle is turning, for safety's sake
- Serial2.print(F("shoulder.lclr_btn.bco2=RED\xFF\xFF\xFF"));
- } else {
- left_limited = false;
- Serial2.print(F("shoulder.left_lim.bco=33816\xFF\xFF\xFF"));
- Serial2.print(F("shoulder.left_lim.txt=\"-----\"\xFF\xFF\xFF"));
- }
- if (wait) {
- while (!Serial2.available()); //Wait for the release event
- Serial2.print(F("shoulder.lclr_btn.bco2=1024\xFF\xFF\xFF"));
- }
- }
- void rightClr(bool wait) {
- //Clear the limit and update the display
- if (spin_rate != SPINDLE_STOPPED) {
- //Refuse if the spindle is turning, for safety's sake
- Serial2.print(F("shoulder.rclr_btn.bco2=RED\xFF\xFF\xFF"));
- } else {
- right_limited = false;
- Serial2.print(F("shoulder.right_lim.bco=33816\xFF\xFF\xFF"));
- Serial2.print(F("shoulder.right_lim.txt=\"-----\"\xFF\xFF\xFF"));
- }
- if (wait) {
- while (!Serial2.available()); //Wait for the release event
- Serial2.print(F("shoulder.rclr_btn.bco2=1024\xFF\xFF\xFF"));
- }
- }
- void zeroSet(bool wait) {
- //Zero the leadscrew and clear the limits.
- //Optionally wait for a button release event from the display.
- if (spin_rate != SPINDLE_STOPPED) {
- //Refuse if the spindle is turning, for safety's sake,
- //because having the spindle suddenly start moving could be bad.
- Serial2.print(F("shoulder.zset_btn.bco2=RED\xFF\xFF\xFF"));
- } else {
- spin_count = 0;
- leadscrew = 0L;
- left_limit = 0L;
- right_limit = 0L;
- leftClr(!waitSerial2);
- rightClr(!waitSerial2);
- }
- if (wait) {
- while (!Serial2.available()); //Wait for the release event
- Serial2.print(F("shoulder.zset_btn.bco2=1024\xFF\xFF\xFF"));
- }
- }
- void jogCheck(bool feed) {
- //Decide whether it's safe to jog
- unsigned long timer = millis();
- long lead;
- while (!Serial2.available()) {
- //The button hasn't been released.
- //Don't jog unless the Nextion button is held for 500ms.
- //This needs to be extended to also jog from the toggle switch.
- if ((millis() - timer) > 500UL) {
- if (spin_rate != SPINDLE_STOPPED) {
- //Refuse if the spindle is turning.
- //I'm considering allowing creeping up on a shoulder and nudging the limit. It's on the list.
- noJog(feed);
- } else {
- //Save the leadscrew position
- lead = leadscrew;
- //Spin the leadscrew
- detachInterrupt(digitalPinToInterrupt(SPINDLE_A)); //No spindle interrupts
- nextionJog(feed);
- //Compensate the leadscrew value by the distance jogged
- jogAdjust(leadscrew - lead);
- attachInterrupt(digitalPinToInterrupt(SPINDLE_A), spindle, FALLING);
- }
- }
- }
- feed_left = feed;
- }
- void nextionRamp(void) {
- //Accelerate until the loop finishes at ICR4_MIN or the button is released
- //This needs a little more work to handle parameter changes better
- unsigned int accel = ICR4_ACCEL;
- unsigned int i, j;
- //Smoothly accelerate
- for ( j = ICR4_MAX ; j != ICR4_MIN ; /* decrement j index in inner loop */ ) {
- for ( i = 32 ; i > 0 ; i--, j -= accel ) {
- _icr4 = j; //ICR4 updates from this in the interrupt handler for smoothness
- if ( Serial2.available() ) break ; //Direction button release message received
- nextionLead();
- delay( 50 ); //Determines the time to ramp up
- }
- if ( Serial2.available() ) break ;
- accel = accel / 2;
- }
- while ( !Serial2.available() ) {
- //The jog button hasn't been released
- //Still jogging at max speed, but need to keep the position updated
- nextionLead();
- delay( 25 );
- }
- }
- void noJog(bool feed) {
- //Change the direction button background RED
- char str[30];
- //Turn left or right button RED
- //Then wait until the button is released
- if (feed) {
- //You're on a page with a left_btn, so no need for the page name
- sprintf(str, "%s%s%s", "left_btn.bco2=", "RED", NX_END);
- Serial2.print(str);
- while (!Serial2.available());
- sprintf(str, "%s%s%s", "left_btn.bco2=", nxDGREEN, NX_END);
- Serial2.print(str);
- } else {
- sprintf(str, "%s%s", "right_btn.bco2=RED", NX_END);
- Serial2.print(str);
- while (!Serial2.available());
- sprintf(str, "%s%s%s", "right_btn.bco2=", nxDGREEN, NX_END);
- Serial2.print(str);
- }
- }
- void nextionFeed(FEED_TABLE *table, int i) {
- //Display the rate and pitch values
- char nxstr[50];
- char rate[20];
- //Changing the rate always clears the fault
- fault = false;
- sprintf(nxstr, "%s%s%s", "rpm.bco=", nxDBLUE, NX_END);
- Serial2.print(nxstr);
- sprintf(rate, "%s%s", table[i].rate, QTNX_END);
- //Unfortunately, this has to be sent to all three pages
- sprintf(nxstr, "%s%s", "start.rate.txt=\"", rate);
- Serial2.print(nxstr);
- sprintf(nxstr, "%s%s", "shoulder.rate.txt=\"", rate);
- Serial2.print(nxstr);
- sprintf(nxstr, "%s%s", "setup.rate.txt=\"", rate);
- Serial2.print(nxstr);
- sprintf(rate, "%s%s", table[i].pitch, QTNX_END);
- sprintf(nxstr, "%s%s", "start.pitch.txt=\"", rate);
- Serial2.print(nxstr);
- sprintf(nxstr, "%s%s", "shoulder.pitch.txt=\"", rate);
- Serial2.print(nxstr);
- sprintf(nxstr, "%s%s", "setup.pitch.txt=\"", rate);
- Serial2.print(nxstr);
- }
- void nextionLeft(void) {
- //"Click" the left direction button
- Serial2.print(F("click left_btn,1\xFF\xFF\xFF"));
- Serial2.print(F("click left_btn,0\xFF\xFF\xFF"));
- }
- void nextionRight(void) {
- //Click the right direction button
- Serial2.print(F("click right_btn,1\xFF\xFF\xFF"));
- Serial2.print(F("click right_btn,0\xFF\xFF\xFF"));
- }
- void nextionDirection(void) {
- //Update the display left/right feed buttons when feed changes direction.
- //Selected feed direction stays "sticky".
- static bool last_spin;
- if (last_spin != spin_ccw) {
- if (FEEDING_LEFT)
- nextionLeft();
- else
- nextionRight();
- }
- last_spin = spin_ccw;
- }
- void nextionUseRPM(void) {
- //Blank the RPM values on the SETUP screen that are too high for the current feed rate
- //"Too high" is defined as driving the stepper faster than 1500rpm.
- int i;
- for ( i=0 ; i<12 ; i++ ) {
- if (steps_per_rev * (long)rpm_table[i] >= STEPPER_LIMIT) {
- rpmHide(i);
- } else {
- rpmShow(i);
- }
- }
- }
- void rpmHide(int i) {
- char str[30];
- sprintf(str, "%s%s%s%s", "setup.", nxrpmID[i], ".pco=33816", NX_END);
- Serial2.print(str);
- }
- void rpmShow(int i) {
- char str[30];
- sprintf(str, "%s%s%s%s", "setup.", nxrpmID[i], ".pco=WHITE", NX_END);
- Serial2.print(str);
- }
- /**************************************
- ***************************************
- *** ***
- *** Support Functions ***
- *** ***
- ***************************************
- **************************************/
- int pitchFind(const char *pitch) {
- //Search the feed table for the pitch string and return the index.
- //I got tired of doing this manually every time I changed the table.
- int i;
- for (i = 0 ; i < INCHES ; i++) {
- if (strcmp(inch[i].pitch, pitch) == 0) return i;
- }
- }
- int rateFind(const char *rate){
- //Search the feed table for the rate string and return the index.
- int i;
- for (i = 0 ; i < METRICS ; i++) {
- if (strcmp(metric[i].rate, rate) == 0) return i;
- }
- }
- void feedSelect(int fmode) {
- //Fill the step lookup table according to mode and feed rate, and update the display.
- //Default to 40tpi, 0.5mm, and lowest diametral and module.
- static int i[4] = {pitchFind(" 40"), rateFind(" 0.5 "), 0, 0};
- switch (fmode) {
- case inch_feed:
- i[fmode] = knobCount(i[fmode], INCHES); //Update and remember the rate
- feedFill(inch[i[fmode]].steps); //Update the lookup table
- //Serial.println(inch[i[fmode]].steps);
- nextionFeed(inch, i[fmode]); //Update the display
- nextionUseRPM();
- break;
- case metric_feed:
- i[fmode] = knobCount(i[fmode], METRICS);
- feedFill(metric[i[fmode]].steps);
- //Serial.println(metric[i[fmode]].steps);
- nextionFeed(metric, i[fmode]);
- nextionUseRPM();
- break;
- case diametral_feed:
- i[fmode] = knobCount(i[fmode], DIAMETRALS);
- feedFill(diametral[i[fmode]].steps);
- //Serial.println(diametral[i[fmode]].steps);
- nextionFeed(diametral, i[fmode]);
- nextionUseRPM();
- break;
- case module_feed:
- i[fmode] = knobCount(i[fmode], MODULES);
- feedFill(module[i[fmode]].steps);
- //Serial.println(module[i[fmode]].steps);
- nextionFeed(module, i[fmode]);
- nextionUseRPM();
- break;
- }
- }
- char *leadStr(long lead, char *leadstr) {
- //Format and return the leadscrew position string
- char sign;
- int whole, fraction;
- if (lead < 0)
- sign = '-';
- else
- sign = ' ';
- if (feed_mode == inch_feed || feed_mode == diametral_feed) {
- //Format in inches
- whole = abs(lead) / LSPI;
- fraction = (abs(lead) * 10 / LSPM10) % 1000; //Scale to 0.001"
- sprintf(leadstr, "%c%d.%03d", sign, whole, fraction );
- } else {
- //Format in millimeters with extra precision
- whole = abs(lead) * 10 / LSPMM10; //Lead is a long, so the long calculation is cast to int
- fraction = (abs(lead) * 10 % LSPMM10) * 100 / LSPMM10; //Scale to .01mm
- sprintf(leadstr, "%c%d.%02d", sign, whole, fraction );
- }
- return (leadstr);
- }
- void feedFill(int steps_per) {
- //Distribute the steps evenly around one spindle rotation
- int i;
- int spsc; //Steps per spindle count
- int rem; //Remainder
- int sum = 0; //For accumulating remainders
- steps_per_rev = steps_per;
- spsc = steps_per / SCPR;
- rem = steps_per % SCPR;
- max_steps = spsc;
- if (rem)
- max_steps++;
- for (i = 0; i < SCPR; i++) {
- step_table[i] = spsc;
- sum += rem;
- //Sum the remainders and carry the one
- if (sum >= SCPR) {
- step_table[i]++;
- sum -= SCPR;
- }
- }
- pwmPeriodSet();
- }
- int spinAvg(unsigned int rate) {
- //Average 16 spindle rate readings for RPM calculation.
- static unsigned int spin[16];
- unsigned long sum = 0L;
- static int i = 0;
- int j;
- spin[i++] = rate;
- if (i == 16)
- i = 0;
- for (j = 0; j < 16; j++)
- sum += spin[j];
- return sum / 16;
- }
- void spinModulus(bool ccw) {
- //Keep track of the spindle position for synchronization
- //Counts between 0 and SCPR-1
- if (ccw) {
- if (spin_count < (SCPR - 1)) {
- spin_count++;
- } else {
- spin_count = 0;
- }
- } else {
- if (spin_count > 0) {
- spin_count--;
- } else {
- spin_count = SCPR - 1;
- }
- }
- }
- void jogAdjust(long jog_count) {
- //Adjust spin_count as if the leadscrew movement had been
- //generated by the spindle turning rather than jogging.
- int jog_adjust;
- //A few counts can get lost to truncation on each jog, but saving them would be complicated.
- //Because jog_count is long, the calculation is performed with long precision.
- //Jogging toward the head produces a negative jog count and jog_adjust value.
- jog_adjust = (jog_count * SCPR / steps_per_rev) % SCPR;
- if ((spin_count -= jog_adjust) < 0) {
- spin_count += SCPR;
- }
- }
- void nextionJog(bool feed) {
- //While the direction button is held, advance the leadscrew with smooth acceleration
- //Save the timer registers
- volatile int icr4 = ICR4;
- volatile int tcnt4 = TCNT4;
- jogging = true;
- pwmOn(feed);
- nextionRamp();
- pwmOff();
- jogging = false;
- TCNT4 = tcnt4; //Restore the timer registers
- ICR4 = icr4;
- }
- void knobCheck(void) {
- //Check for encoder knob ticks
- if (knob_count) {
- feedSelect(feed_mode);
- nextionUseRPM();
- }
- }
- int knobCount(int i, int table_size) {
- //Knob count doesn't really matter, only direction and table limits
- if (knob_count > 0) {
- if (i < table_size - 1)
- ++i;
- } else if (knob_count < 0) {
- if (i > 0)
- --i;
- }
- knob_count = 0;
- return i;
- }
- void toggleCheck(void) {
- //Check the state of the feed direction switch (active low).
- //I intend to add the jog function to the toggle switch, but haven't gotten to it yet.
- unsigned long timer = millis();
- long lead;
- volatile int this_left = digitalRead(LEFT_MOM);
- volatile int this_right = digitalRead(RIGHT_MOM);
- static int last_left;
- static int last_right;
- if (FEEDING_LEFT && (this_right == LOW)) { //No need to do anything unless direction changes
- if (last_right == LOW) { //Very simple debounce
- if (spin_ccw) {
- feed_left = false;
- } else {
- feed_left = true;
- }
- nextionRight();
- }
- } else if (!FEEDING_LEFT && (this_left == LOW)) {
- if (last_left == LOW) {
- if (spin_ccw) {
- feed_left = true;
- } else {
- feed_left = false;
- }
- nextionLeft();
- }
- }
- last_left = this_left;
- last_right = this_right;
- }
- /********************************************************
- *********************************************************
- *** ***
- *** REGISTER MANIPULATION ***
- *** ***
- *********************************************************
- ********************************************************/
- void pcint4Enab(void) {
- //Interrupt on PB4 (Pin 21) falling edge from the knob
- EICRB |= _BV(ISC41);
- EIMSK |= _BV(INT4);
- }
- void tc3Init(void) {
- // Configure TC3 for checking spindle speed
- TCCR3A = _BV(COM3A0); //Because the high byte doesn't increment in '00' mode
- TCCR3B = _BV(CS30); //Run TCNT3 at 16Mhz to get better RPM resolution at high speed
- tc3.low = TCNT3; //Initialize to the current value of the counter
- }
- void tc3Enab(void) {
- //Enable TC3 overflow interrupt
- TIMSK3 = _BV(TOIE3);
- }
- void tc4Init(void) {
- //TC4 generates the step pulses to drive the leadscrew
- ICR4 = STP_MIN; //Period defaults to minimum
- OCR4A = PUL_MIN; //Pulse width is constant
- TCCR4B = _BV(WGM43) | _BV(WGM42); //Set Fast PWM Mode 14 with the clock off
- PORTH |= _BV(DIR_N) | _BV(PUL_N); //Set high or it glitches low when enabled
- DDRH |= _BV(DDH4) | _BV(DDH3); //Set timer pins to output
- TCCR4A = _BV(WGM41) | _BV(COM4A1) | _BV(COM4A0); //Set on compare match
- TCNT4 = period_list[max_steps] - PUL_MIN; //Preload the (stopped) counter for immediate pulse
- }
- void tc4Enab(void) {
- //Enable TC4 overflow interrupt
- TIMSK4 = _BV(OCIE4A);
- }
- void pwmOn(bool feed) {
- //Fast PWM Mode 14, inverting
- //Start by setting the direction bit
- if (feed) {
- PORTH |= _BV(DIR_N);
- } else {
- PORTH &= ~_BV(DIR_N);
- }
- //TOP in ICR4A, start the 2Mhz clock
- TCCR4B = _BV(WGM43) | _BV(WGM42) | _BV(CS41);
- }
- void pwmOff(void) {
- //Just turn off the clock
- TCCR4B = _BV(WGM43) | _BV(WGM42);
- }
- void pwmPeriodSet(void) {
- //Set the period value according to the number of steps per spindle tick.
- //Update ICR4 with interrupts off since it's not double-buffered.
- noInterrupts();
- ICR4 = period_list[max_steps];
- interrupts();
- }
- /********************************************************
- *********************************************************
- *** ***
- *** INTERRUPT HANDLING ***
- *** ***
- *********************************************************
- ********************************************************/
- void spindle(void) {
- //This interrupt is called on falling edges of SPINDLE_A
- bool feeding_left;
- static bool last_feed = feed_left; //Just for the first time
- // First check if all steps have been sent from the last time.
- // If steps are left over, the spindle is going too fast for the feed rate.
- // A fault here will cause the displayed RPM to turn red, but nothing stops.
- // Things can get strange with the driver if the difference is too extreme.
- // Several times it has seemed to trip something so that both driver status LED's went out.
- // Turning it off for a couple of minutes has reset it so far, so thermal protection?
- if (steps != 0)
- fault = true;
- // SPINDLE_A brought us here, now determine the direction it's turning.
- // The lathe's reverse lever position isn't known. I keep it in the latched-down position.
- // Adding a microswitch to the encoder bracket to sense it might be a future enhancement.
- // I do the simplest possible decoding of direction by simply looking at the state of phase B,
- // which was much simpler and faster than full quadrature decoding.
- // I started off with a 200 pulse-per-revolution rotary encoder and full decoding,
- // but switched to an 800p/r encoder and this scheme for the equivalent resolution.
- // Full decoding may have better noise immunity, but I haven't had any problems with this
- // since adding 2k external pull-up resistors to the Arduino inputs to speed up the rising edges.
- // The Arduino's internal pull-ups were just too big and slow.
- if (digitalRead(SPINDLE_B)) { //Spindle is turning CCW if B is high
- spinModulus(spin_ccw = true);
- } else {
- spinModulus(spin_ccw = false);
- }
- if (feed_left != last_feed) {
- //Changed direction, so remember sync count
- synced = false;
- //Keep the sync value between 0 and SCPR-1
- if (spin_count) { //Non-zero
- sync_count = SCPR - spin_count;
- } else {
- sync_count = 0;
- }
- }
- //Save a few clock cycles later
- feeding_left = (spin_ccw && feed_left) || (!spin_ccw && !feed_left);
- if (feeding_left) {
- if (synced) {
- //Is feeding left possible?
- if ( !left_limited || (leadscrew > left_limit) ) {
- //Turn on PWM if steps is non-zero.
- if (steps = step_table[spin_count]) {
- pwmOn(feeding_left);
- }
- } else {
- //The left limit was just reached
- synced = false;
- if (spin_count) {
- lsync_count = SCPR - spin_count;
- } else {
- lsync_count = 0;
- }
- }
- } else if (right_limited && (leadscrew >= right_limit)) {
- //Hit the right limit last time, so wait for sync before going left
- if (spin_count == rsync_count) {
- synced = true;
- }
- } else if (!left_limited || (leadscrew > left_limit)) {
- //It was an on-the-fly direction change
- if (spin_count == sync_count) {
- synced = true;
- }
- }
- } else {
- if (synced) {
- if ( !right_limited || (leadscrew < right_limit) ) {
- if (steps = step_table[spin_count]) {
- pwmOn(feeding_left);
- }
- } else {
- synced = false;
- if (spin_count) {
- rsync_count = SCPR - spin_count;
- } else {
- rsync_count = 0;
- }
- }
- } else if (left_limited && (leadscrew <= left_limit)) {
- if (spin_count == lsync_count) {
- synced = true;
- }
- } else if (!right_limited || (leadscrew < right_limit)) {
- if (spin_count == sync_count) {
- synced = true;
- }
- }
- }
- last_feed = feed_left;
- tc3.low = TCNT3; //tc3.high is handled by the counter overflow interrupt
- spin_rate = tc3.count - last_tc3;
- last_tc3 = tc3.count;
- }
- void knob(void) {
- //This interrupt is called by KNOB_A
- //It really just keeps track of the direction of the click.
- if (digitalRead(KNOB_B)) {
- knob_count-- ;
- } else {
- knob_count++ ;
- }
- }
- ISR(TIMER3_OVF_vect) {
- // TIMER3 provides the master clock for determining spindle speed.
- // This interrupt counts 16-bit timer overflows to extend the precision.
- // It also provides a convenient place to check whether the spindle is moving.
- tc3.count += 0x10000;
- // Detect that the spindle has stopped
- if (spin_rate == last_spin)
- spin_rate = SPINDLE_STOPPED;
- last_spin = spin_rate;
- }
- ISR(TIMER4_COMPA_vect) {
- // This stops the PWM if all the steps for a spindle tick have been output,
- // resets the counter for the next spindle tick according to feed rate,
- // and keeps track of the carriage movement.
- static int i;
- if (!jogging) {
- if (--steps == 0) { //Get out fast if there's another step coming
- pwmOff();
- //The clock is stopped, so TCNT4 is no longer incrementing.
- //Now, preload TCNT4 so that the first step pulse will be
- //output immediately when the clock is turned on by pwmOn().
- //This provides more margin after the last step is output.
- TCNT4 = period_list[max_steps] - PUL_MIN;
- //When feeding, the granularity of the leadscrew value
- //is determined by counts per spindle tick.
- if (FEEDING_LEFT) {
- leadscrew -= step_table[spin_count];
- } else {
- leadscrew += step_table[spin_count];
- }
- }
- } else {
- //Synchronously update ICR4 here for smoother acceleration
- ICR4 = _icr4;
- //When jogging, the leadscrew is incremented/decremented by one
- if (PORTH & _BV(DIR_N)) { //Cheat a little here and read the bit directly
- --leadscrew;
- } else {
- ++leadscrew;
- }
- }
- }
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- // Copyright (c) 2020 Jon Bryan
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in all
- // copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- // SOFTWARE.
- #ifndef __CONFIGURATION_H
- #define __CONFIGURATION_H
- //Added RPM blanking 04/22/2020:jrb
- //================================================================================
- // Output Pins
- //================================================================================
- #define PUL_N PORTH3 //Stepper PUL- is on Pin 6, active low, with PUL+ tied to +5V
- #define DIR_N PORTH4 //Stepper DIR- is on Pin 7, active low, with DIR+ tied to +5V
- //================================================================================
- // Nextion miscellaneous
- //================================================================================
- #define nxPBLUE "44415" //Pale blue
- #define nxDBLUE "33816" //Dark blue
- #define nxDGREEN "1024" //Dark green
- //Nextion display closing quote and three-character terminator
- #define QTNX_END "\"\xFF\xFF\xFF"
- //Without the closing quote
- #define NX_END "\xFF\xFF\xFF"
- //Component ID's for the Nextion "buttons"
- enum nextionID {
- inch_btn = 1,
- metric_btn,
- diametral_btn,
- module_btn,
- left_btn,
- right_btn,
- back_btn,
- lset_btn,
- zset_btn,
- rset_btn,
- lclr_btn = 27, //Too much trouble to edit the display to keep them all in sequence
- rclr_btn
- };
- //ID's for the RPM text boxes
- //These are placed in the display with the ID's in order of descending RPM
- char nxrpmID[12][4] {
- "t3", "t4", "t5", "t6", "t7", "t8",
- "t9", "t10", "t11", "t12", "t13", "t14"
- };
- //================================================================================
- //Input Pins
- //================================================================================
- // Wired to PE4/INT4 and PE5/INT5 on Mega2560 but Arduino remaps it for compatibility
- #define SPINDLE_B 2 //Encoder Phase B pin (PORTE4) pulled up with 2k
- #define SPINDLE_A 3 //Encoder Phase A pin (PORTE5) pulled up with 2k
- #define LEFT_MOM 18 //Direction toggle switch
- #define RIGHT_MOM 19 //Direction toggle switch
- #define KNOB_B 20 //Knob Phase B pin (PORTD1) through 1k/0.1uf RC filter
- #define KNOB_A 21 //Knob Phase A pin (PORTD0) through 1k/0.1uf RC filter
- #define ALARM 13 //Microstepper controller ALARM open collector (not yet implemented)
- //================================================================================
- // Scaler magic numbers
- //================================================================================
- #define SCPR 1000//800 //Spindle encoder Counts Per Revolution
- #define MICROSTEPS 400 //Driver microsteps per revolution
- #define STEP_RATIO 8 //Stepper:Leadscrew ratio
- #define LTPI 8 //Leadscrew Threads Per Inch
- #define LSPI 25600 //Leadscrew Steps Per Inch (LTPI*MICROSTEPS*STEP_RATIO)
- #define LSPM10 256 //Leadscrew Steps Per Mil (0.001") * 10
- #define LSPMM10 10079 //Leadscrew Steps Per MilliMeter (LSPI / 25.4 * 10)
- //================================================================================
- // Timing
- //================================================================================
- #define STP_MIN 60 //30us period minimum to accommodate jitter (2Mhz clock)
- #define PUL_MIN 6 //3us pulse minimum for stepper drive
- #define RPM20 60000 //16Mhz clock ticks at 20rpm
- // Timer 3 Counts Per Minute for calculating spindle RPM
- #define T3CPM (16000000L * 60L) //TC3 counts per minute, 16Mhz * 60 seconds
- //================================================================================
- // Timer Parameters for Jogging
- //================================================================================
- #define ICR4_MAX 0x8000 //Slow enough to adjust by thousandths
- #define ICR4_MIN 0x100 //Any faster can cause the microstepper to error out
- #define ICR4_ACCEL 0x200 //Initial increment for acceleration
- //================================================================================
- // "Slowest possible" value to indicate that the spindle is stopped
- //================================================================================
- #define SPINDLE_STOPPED 0xFFFF //Set when the spindle isn't moving
- //================================================================================
- // Steps per minute to spin the stepper motor at a "conservative" 1500rpm.
- // That doesn't mean that you can't get away with running a little faster.
- // I settled on 1500rpm because it provides some margin, and people will
- // always push the speed limit, don't you know. The torque curve on the
- // 3.5Nm Nema 24 motor that I'm using is plotted up to 2000rpm.
- // It's really starting to whistle at that speed, though.
- //================================================================================
- #define STEPPER_LIMIT 600000L
- //================================================================================
- // Flags
- //================================================================================
- bool feed_left = true; //Feed toward the headstock
- bool spin_ccw = true; //Spindle direction
- bool fault = false; //Step overrun flag
- bool jogging = false; //Flag to control timer interrupt behavior
- bool right_limited = false; //Flag to control feed limits
- bool left_limited = false; //Flag to control feed limits
- bool synced = true; //Flag for synchronizing the leadscrew with the spindle
- bool waitSerial2 = true; //For zeroSet()
- int knob_count; //Knob counts (really just direction)
- int spin_count = 0; //Value from 0-799 (counts per rev)
- int sync_count = 0; //On-the-fly spindle count for synchronizing
- int lsync_count = 0; //Spindle count upon reaching the left limit
- int rsync_count = 0; //Likewise for the right
- int steps_per_rev; //Steps per spindle revolution for the current pitch
- long leadscrew; //Leadscrew counts
- long left_limit = 0L; //Leadscrew value for left limit when enabled
- long right_limit = 0L; //Leadscrew value for right limit
- unsigned int spin_rate = SPINDLE_STOPPED;
- unsigned int last_spin;
- //================================================================================
- // Feed direction logic
- //================================================================================
- #define FEEDING_LEFT ((spin_ccw && feed_left) || (!spin_ccw && !feed_left))
- //================================================================================
- // Feed modes
- //================================================================================
- enum {
- inch_feed,
- metric_feed,
- diametral_feed,
- module_feed
- } feed_mode = inch_feed ;
- //================================================================================
- // Timers
- //================================================================================
- union {
- // Used to extend the resolution of TC3
- unsigned long count;
- unsigned int low, high;
- } tc3 ;
- unsigned long last_tc3 = 0; //This is used to calculate spindle RPM.
- unsigned int last_step = 0; //For determining pulse rate
- unsigned int _icr4; //Buffered value for ICR4 used during jogging
- byte steps; //Steps per spindle tick
- byte step_table[SCPR]; //Lookup table for precalculated feed rate
- int max_steps; //Maximum steps per spindle tick, used in several ways
- //The measured spindle speeds on my lathe with a 1720rpm motor
- unsigned int rpm_table[12] { 1430, 812, 648, 463, 368, 238, 210, 135, 108, 77, 61, 34 };
- // "Official" values: 1450, 780, 620, 420, 334, 244, 179, 131, 104, 70, 56, 30
- // The highest-speed entry in the Nextion display has an ID of "t3",
- // and the lowest is "t14", and they are in order from highest to lowest RPM.
- // To blank the highest entry send "setup.t3.pco=33816" to turn the text blue,
- // then to unblank it send "setup.t3.pco=WHITE" to turn it white again.
- // This chart is reorganized from the Model 200 manual to reflect the physical belt position.
- // Eventually I want this chart to display/highlight RPM's that are valid for the feed rate.
- //
- // --------------------------------------------------------------------|
- // MOTOR | SPINDLE BELT POSITION |
- // BELT |----------------------------------------------------------|
- // POSITION | DIRECT BELT DRIVE | BACK GEAR DRIVE |
- // ---------|-----------------------------|----------------------------|
- // HIGH | 1450 | 780 | 420 | 244 | 131 | 70 |
- // ---------|---------|---------|---------|---------|---------|--------|
- // LOW | 620 | 334 | 179 | 104 | 56 | 30 |
- // --------------------------------------------------------------------|
- //For completeness I tried to include every tap pitch available from McMaster-Carr,
- //and every pitch that I could identify in pictures of old lathes.
- //I even included metric "British Association" pitches once I knew about them.
- //The maximum feed rate in each mode is limited to about 8000 steps per rev.
- //There are practical limits on spindle speed for given feed rates.
- //It worked well to scale the RPM speed steps directly as the steps per spindle tick increases.
- //A rate of 0.025" per rev moves the carriage 0.6" per second at 1450rpm, a fast clip.
- //That's the 1:1 rate, when a step is output for every spindle tick.
- //A 0.050" feed at 780rpm is moving 0.65" per second, 0.075" at 620rpm is 0.775" etc.
- //At the max inch rate of 0.25" per rev at 70rpm, that's fallen off to about 0.29" per rev.
- //That seems sensible, since the forces go up with the deeper corresponding cut.
- //We'll see how it works out in practice.
- //I experimented with dynamically varying the step period, but it just got too fussy.
- //Maximum steps per spindle tick is 11, a carriage movement of 0.00043" (fine enough for me).
- // Period is roughly scaled to feed rate, for smoothing and to mitigate resonances.
- // 2Mhz / ((RPM*SCPR)/60) / max_steps
- //int period_list[12] { STP_MIN, STP_MIN, 96, 81, 90, 90, 69, 120, 143, 160, 214, 244 };
- // 80% of 2Mhz / ((RPM*SCPR)/60) / max_steps
- //So after all that I wound up looking at it on the oscilloscope and picking arbitrary values.
- //Maybe I'll revisit this at a later date:
- int period_list[12] {
- STP_MIN, STP_MIN, STP_MIN, STP_MIN, STP_MIN, STP_MIN,
- STP_MIN, STP_MIN, 83, 104, 155, 194 };
- #endif // __CONFIGURATION_H
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- // Copyright (c) 2020 Jon Bryan
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in all
- // copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- // SOFTWARE.
- #ifndef __TABLES_H
- #define __TABLES_H
- // Change Log:
- // Added British Association threads to the Metric table 01/28/2020:JRB
- // Added British "Cycle Engineering Institute" 62tpi 04/04/2020:JRB
- //================================================================================
- // Lookup tables
- //================================================================================
- struct FEED_TABLE {
- unsigned int steps; //Encoder steps per spindle revolution for a given pitch //Kroki enkodera na obrót wrzeciona dla danego skoku
- char rate[7]; //Feed rate in inches or millimeters //Szybkość posuwu w calach lub milimetrach
- char pitch[5]; //Threads per inch or special designations like "10BA" //Gwinty na cal lub specjalne oznaczenia, takie jak „10BA”
- };
- // INCH mode steps per revolution = leadscrew steps per inch / pitch //Kroki w trybie CALOWYM na obrót = kroki śruby pociągowej na cal / skok
- #define INCH_STEPS(tpi) ((unsigned int) round((float)LSPI/(tpi)))
- // METRIC mode steps per revolution = rate_mm * leadscrew steps per inch / 25.4 //Metryczne kroki na obrót = stopa_mm * kroki śruby pociągowej na cal / 25,4
- #define MM_STEPS(mm) ((unsigned int) round((float)(mm)*LSPI/25.4))
- // DIAMETRAL mode steps per revolution = pi * leadscrew steps per inch / diametral pitch
- #define DIAM_STEPS(dpi) ((unsigned int) round(PI*(float)LSPI/(dpi)))
- // MODULE mode steps per revolution = pi * rate_mm * LSPI / 25.4
- #define MOD_STEPS(mm) ((unsigned int) round(PI*(float)(mm)*LSPI/25.4))
- //***********************************************************************************
- // Following are the lookup tables for the number of steps per spindle tick. *
- // Steps per spindle tick for a given rate/pitch are precalculated for efficiency. *
- //***********************************************************************************
- const int INCHES = 76; //Number of entries in the inch feed table
- FEED_TABLE inch[INCHES] = {
- {INCH_STEPS(2000), "0.0005", "----"}, /* 0 */
- {INCH_STEPS(1000), "0.001 ", "----"}, /* 1 */
- {INCH_STEPS(667), "0.0015", "----"}, /* 2 */
- {INCH_STEPS(500), "0.002 ", "----"}, /* 3 */
- {INCH_STEPS(400), "0.0025", "----"}, /* 4 */
- {INCH_STEPS(333), "0.003 ", "----"}, /* 5 */
- {INCH_STEPS(286), "0.0035", "----"}, /* 6 */
- {INCH_STEPS(250), "0.004 ", "----"}, /* 7 */
- {INCH_STEPS(240), "0.0042", " 240"}, /* 8 */
- {INCH_STEPS(224), "0.0045", " 224"}, /* 9 */
- {INCH_STEPS(216), "0.0046", " 216"}, /* 10 */
- {INCH_STEPS(208), "0.0048", " 208"}, /* 11 */
- {INCH_STEPS(192), "0.0052", " 192"}, /* 12 */
- {INCH_STEPS(184), "0.0054", " 184"}, /* 13 */
- {INCH_STEPS(176), "0.0057", " 176"}, /* 14 */
- {INCH_STEPS(160), "0.0062", " 160"}, /* 15 */
- {INCH_STEPS(144), "0.0069", " 144"}, /* 16 */
- {INCH_STEPS(128), "0.0078", " 128"}, /* 17 */
- {INCH_STEPS(120), "0.0083", " 120"}, /* 18 */
- {INCH_STEPS(112), "0.0089", " 112"}, /* 19 */
- {INCH_STEPS(108), "0.0093", " 108"}, /* 20 */
- {INCH_STEPS(104), "0.0096", " 104"}, /* 21 */
- {INCH_STEPS(100), "0.010 ", " 100"}, /* 22 */
- {INCH_STEPS(96), "0.0104", " 96"}, /* 23 */
- {INCH_STEPS(92), "0.0109", " 92"}, /* 24 */
- {INCH_STEPS(90), "0.0111", " 90"}, /* 25 */
- {INCH_STEPS(88), "0.0114", " 88"}, /* 26 */
- {INCH_STEPS(80), "0.0125", " 80"}, /* 27 */
- {INCH_STEPS(72), "0.0139", " 72"}, /* 28 */
- {INCH_STEPS(70), "0.0143", " 70"}, /* 29 */
- {INCH_STEPS(64), "0.0156", " 64"}, /* 30 */
- {INCH_STEPS(62), "0.0161", " 62"}, /* 31 */ //CEI thread (old spoke nipples mostly, I guess)
- {INCH_STEPS(60), "0.0167", " 60"}, /* 32 */
- {INCH_STEPS(56), "0.0179", " 56"}, /* 33 */
- {INCH_STEPS(54), "0.0185", " 54"}, /* 34 */
- {INCH_STEPS(52), "0.0192", " 52"}, /* 35 */
- {INCH_STEPS(50), "0.020 ", " 50"}, /* 36 */
- {INCH_STEPS(48), "0.0208", " 48"}, /* 37 */
- {INCH_STEPS(46), "0.0217", " 46"}, /* 38 */
- {INCH_STEPS(44), "0.0227", " 44"}, /* 39 */
- {INCH_STEPS(40), "0.025 ", " 40"}, /* 40 */
- {INCH_STEPS(36), "0.0278", " 36"}, /* 41 */
- {INCH_STEPS(32), "0.0312", " 32"}, /* 42 */
- {INCH_STEPS(30), "0.0333", " 30"}, /* 43 */
- {INCH_STEPS(28), "0.0357", " 28"}, /* 44 */
- {INCH_STEPS(27), "0.037 ", " 27"}, /* 45 */
- {INCH_STEPS(26), "0.0385", " 26"}, /* 46 */
- {INCH_STEPS(25), "0.040 ", " 25"}, /* 47 */
- {INCH_STEPS(24), "0.0417", " 24"}, /* 48 */
- {INCH_STEPS(23), "0.0434", " 23"}, /* 49 */
- {INCH_STEPS(22), "0.0454", " 22"}, /* 50 */
- {INCH_STEPS(20), "0.050 ", " 20"}, /* 51 */
- {INCH_STEPS(19), "0.0526", " 19"}, /* 52 */ // British Standard Pipe or "G" thread
- {INCH_STEPS(18), "0.0555", " 18"}, /* 53 */
- {INCH_STEPS(17.5), "0.0571", " 17\xBD"}, /* 54 */ // xBD is the character for "1/2"
- {INCH_STEPS(16), "0.0625", " 16"}, /* 55 */
- {INCH_STEPS(15), "0.0667", " 15"}, /* 56 */
- {INCH_STEPS(14), "0.0714", " 14"}, /* 57 */
- {INCH_STEPS(13.5), "0.0741", " 13\xBD"}, /* 58 */
- {INCH_STEPS(13), "0.0769", " 13" }, /* 59 */
- {INCH_STEPS(12), "0.0833", " 12" }, /* 60 */
- {INCH_STEPS(11.5), "0.087 ", " 11\xBD"}, /* 61 */
- {INCH_STEPS(11), "0.0909", " 11" }, /* 62 */
- {INCH_STEPS(10), "0.100 ", " 10" }, /* 63 */
- {INCH_STEPS(9), "0.1111", " 9" }, /* 64 */
- {INCH_STEPS(8), "0.125 ", " 8" }, /* 65 */
- {INCH_STEPS(7.5), "0.1333", " 7\xBD"}, /* 66 */
- {INCH_STEPS(7), "0.1429", " 7" }, /* 67 */
- {INCH_STEPS(6.75), "0.1481", " 6\xBE"}, /* 68 */ // xBE is the character for "3/4"
- {INCH_STEPS(6.5), "0.1538", " 6\xBD"}, /* 69 */
- {INCH_STEPS(6), "0.1667", " 6" }, /* 70 */
- {INCH_STEPS(5.75), "0.1739", " 5\xBE"}, /* 71 */
- {INCH_STEPS(5.5), "0.1818", " 5\xBD"}, /* 72 */
- {INCH_STEPS(5), "0.200 ", " 5" }, /* 73 */
- {INCH_STEPS(4.5), "0.2222", " 4\xBD"}, /* 74 */
- {INCH_STEPS(4), "0.250 ", " 4" } /* 75 */
- };
- const int METRICS = 56 ;
- FEED_TABLE metric[METRICS] = {
- {MM_STEPS(0.01), " 0.01", "----"}, /* 0 */
- {MM_STEPS(0.02), " 0.02", "----"}, /* 1 */
- {MM_STEPS(0.03), " 0.03", "----"}, /* 2 */
- {MM_STEPS(0.04), " 0.04", "----"}, /* 3 */
- {MM_STEPS(0.05), " 0.05", "----"}, /* 4 */
- {MM_STEPS(0.06), " 0.06", "----"}, /* 5 */
- {MM_STEPS(0.07), " 0.07", "----"}, /* 6 */
- {MM_STEPS(0.08), " 0.08", "----"}, /* 7 */
- {MM_STEPS(0.09), " 0.09", "----"}, /* 8 */
- {MM_STEPS(0.10), " 0.1 ", "----"}, /* 9 */
- {MM_STEPS(0.12), " 0.12", "----"}, /* 10 */
- {MM_STEPS(0.15), " 0.15", "----"}, /* 11 */
- {MM_STEPS(0.20), " 0.2 ", "----"}, /* 12 */
- {MM_STEPS(0.225), " 0.225", "----"}, /* 13 */
- {MM_STEPS(0.25), " 0.25", "----"}, /* 14 */
- {MM_STEPS(0.30), " 0.3 ", "----"}, /* 15 */
- {MM_STEPS(0.35), " 0.35", "10BA"}, /* 16 */ // British Association Thread
- {MM_STEPS(0.39), " 0.39", " 9BA"}, /* 17 */
- {MM_STEPS(0.40), " 0.4 ", "----"}, /* 18 */
- {MM_STEPS(0.43), " 0.43", " 8BA"}, /* 19 */
- {MM_STEPS(0.45), " 0.45", "----"}, /* 20 */
- {MM_STEPS(0.48), " 0.48", " 7BA"}, /* 21 */
- {MM_STEPS(0.50), " 0.5 ", "----"}, /* 22 */
- {MM_STEPS(0.53), " 0.53", " 6BA"}, /* 23 */
- {MM_STEPS(0.55), " 0.55", "----"}, /* 24 */
- {MM_STEPS(0.59), " 0.59", " 5BA"}, /* 25 */
- {MM_STEPS(0.60), " 0.6 ", "----"}, /* 26 */
- {MM_STEPS(0.65), " 0.65", "----"}, /* 27 */
- {MM_STEPS(0.66), " 0.66", " 4BA"}, /* 28 */
- {MM_STEPS(0.70), " 0.7 ", "----"}, /* 29 */
- {MM_STEPS(0.73), " 0.73", " 3BA"}, /* 30 */
- {MM_STEPS(0.75), " 0.75", "----"}, /* 31 */
- {MM_STEPS(0.80), " 0.8 ", "----"}, /* 32 */
- {MM_STEPS(0.81), " 0.81", " 2BA"}, /* 33 */
- {MM_STEPS(0.90), " 0.9 ", " 1BA"}, /* 34 */
- {MM_STEPS(1.00), " 1.0 ", " 0BA"}, /* 35 */
- {MM_STEPS(1.10), " 1.1 ", "----"}, /* 36 */
- {MM_STEPS(1.20), " 1.2 ", "----"}, /* 37 */
- {MM_STEPS(1.25), " 1.25", "----"}, /* 38 */
- {MM_STEPS(1.30), " 1.3 ", "----"}, /* 39 */
- {MM_STEPS(1.40), " 1.4 ", "----"}, /* 40 */
- {MM_STEPS(1.50), " 1.5 ", "----"}, /* 41 */
- {MM_STEPS(1.75), " 1.75", "----"}, /* 42 */
- {MM_STEPS(2.00), " 2.0 ", "----"}, /* 43 */
- {MM_STEPS(2.25), " 2.25", "----"}, /* 44 */
- {MM_STEPS(2.50), " 2.5 ", "----"}, /* 45 */
- {MM_STEPS(2.75), " 2.75", "----"}, /* 46 */
- {MM_STEPS(3.00), " 3.0 ", "----"}, /* 47 */
- {MM_STEPS(3.25), " 3.25", "----"}, /* 48 */
- {MM_STEPS(3.50), " 3.5 ", "----"}, /* 49 */
- {MM_STEPS(4.00), " 4.0 ", "----"}, /* 50 */
- {MM_STEPS(4.50), " 4.5 ", "----"}, /* 51 */
- {MM_STEPS(5.00), " 5.0 ", "----"}, /* 52 */
- {MM_STEPS(5.50), " 5.5 ", "----"}, /* 53 */
- {MM_STEPS(6.00), " 6.0 ", "----"}, /* 54 */
- {MM_STEPS(6.50), " 6.5 ", "----"} /* 55 */
- };
- const int DIAMETRALS = 38;
- FEED_TABLE diametral[DIAMETRALS] = {
- {DIAM_STEPS(120), "0.0262", " 120"}, /* 0 */
- {DIAM_STEPS(112), "0.0280", " 112"}, /* 1 */
- {DIAM_STEPS(108), "0.0291", " 108"}, /* 2 */
- {DIAM_STEPS(104), "0.0302", " 104"}, /* 3 */
- {DIAM_STEPS( 96), "0.0327", " 96"}, /* 4 */
- {DIAM_STEPS( 92), "0.0341", " 92"}, /* 5 */
- {DIAM_STEPS( 88), "0.0357", " 88"}, /* 6 */
- {DIAM_STEPS( 80), "0.0393", " 80"}, /* 7 */
- {DIAM_STEPS( 76), "0.0413", " 76"}, /* 8 */
- {DIAM_STEPS( 72), "0.0436", " 72"}, /* 9 */
- {DIAM_STEPS( 64), "0.0491", " 64"}, /* 10 */
- {DIAM_STEPS( 60), "0.0524", " 60"}, /* 11 */
- {DIAM_STEPS( 56), "0.0561", " 56"}, /* 12 */
- {DIAM_STEPS( 54), "0.0582", " 54"}, /* 13 */
- {DIAM_STEPS( 52), "0.0604", " 52"}, /* 14 */
- {DIAM_STEPS( 48), "0.0654", " 48"}, /* 15 */
- {DIAM_STEPS( 46), "0.0683", " 46"}, /* 16 */
- {DIAM_STEPS( 44), "0.0714", " 44"}, /* 17 */
- {DIAM_STEPS( 40), "0.0785", " 40"}, /* 18 */
- {DIAM_STEPS( 38), "0.0827", " 38"}, /* 19 */
- {DIAM_STEPS( 36), "0.0873", " 36"}, /* 20 */
- {DIAM_STEPS( 32), "0.0912", " 32"}, /* 21 */
- {DIAM_STEPS( 30), "0.1041", " 30"}, /* 22 */
- {DIAM_STEPS( 28), "0.1122", " 28"}, /* 23 */
- {DIAM_STEPS( 27), "0.1164", " 27"}, /* 24 */
- {DIAM_STEPS( 26), "0.1208", " 26"}, /* 25 */
- {DIAM_STEPS( 24), "0.1309", " 24"}, /* 26 */
- {DIAM_STEPS( 23), "0.1366", " 23"}, /* 27 */
- {DIAM_STEPS( 22), "0.1428", " 22"}, /* 28 */
- {DIAM_STEPS( 20), "0.1571", " 20"}, /* 29 */
- {DIAM_STEPS( 19), "0.1653", " 19"}, /* 30 */
- {DIAM_STEPS( 18), "0.1745", " 18"}, /* 31 */
- {DIAM_STEPS( 16), "0.1963", " 16"}, /* 32 */
- {DIAM_STEPS( 15), "0.2094", " 15"}, /* 33 */
- {DIAM_STEPS( 14), "0.2244", " 14"}, /* 34 */
- {DIAM_STEPS(13.5), "0.2327", " 13\xBD"}, /* 35 */
- {DIAM_STEPS( 13), "0.2417", " 13" }, /* 36 */
- {DIAM_STEPS( 12), "0.2618", " 12" } /* 37 */
- };
- const int MODULES = 26;
- FEED_TABLE module[MODULES] = {
- {MOD_STEPS(0.20), " 0.2 ", "----"}, /* 0 */
- {MOD_STEPS(0.25), " 0.25", "----"}, /* 1 */
- {MOD_STEPS(0.30), " 0.3 ", "----"}, /* 2 */
- {MOD_STEPS(0.35), " 0.35", "----"}, /* 3 */
- {MOD_STEPS(0.40), " 0.4 ", "----"}, /* 4 */
- {MOD_STEPS(0.45), " 0.45", "----"}, /* 5 */
- {MOD_STEPS(0.50), " 0.5 ", "----"}, /* 6 */
- {MOD_STEPS(0.55), " 0.55", "----"}, /* 7 */
- {MOD_STEPS(0.60), " 0.6 ", "----"}, /* 8 */
- {MOD_STEPS(0.65), " 0.65", "----"}, /* 9 */
- {MOD_STEPS(0.70), " 0.7 ", "----"}, /* 10 */
- {MOD_STEPS(0.80), " 0.8 ", "----"}, /* 11 */
- {MOD_STEPS(0.90), " 0.9 ", "----"}, /* 12 */
- {MOD_STEPS(0.95), " 0.95", "----"}, /* 13 */
- {MOD_STEPS(1.00), " 1.0 ", "----"}, /* 14 */
- {MOD_STEPS(1.10), " 1.1 ", "----"}, /* 15 */
- {MOD_STEPS(1.20), " 1.2 ", "----"}, /* 16 */
- {MOD_STEPS(1.25), " 1.25", "----"}, /* 17 */
- {MOD_STEPS(1.30), " 1.3 ", "----"}, /* 18 */
- {MOD_STEPS(1.40), " 1.4 ", "----"}, /* 19 */
- {MOD_STEPS(1.50), " 1.5 ", "----"}, /* 20 */
- {MOD_STEPS(1.60), " 1.6 ", "----"}, /* 21 */
- {MOD_STEPS(1.75), " 1.75", "----"}, /* 22 */
- {MOD_STEPS(1.80), " 1.8 ", "----"}, /* 23 */
- {MOD_STEPS(1.90), " 1.9 ", "----"}, /* 24 */
- {MOD_STEPS(2.00), " 2.0 ", "----"} /* 25 */
- };
- #endif // __TABLES_H