smoothie port to mbed online compiler (smoothieware.org)
For documentation, license, ..., please check http://smoothieware.org/
This version has been tested with a 3 axis machine
Revision 0:31e91bb0ef3c, committed 2012-07-31
- Comitter:
- scachat
- Date:
- Tue Jul 31 21:11:18 2012 +0000
- Commit message:
- smoothie port to mbed online compiler
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ADC/adc.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,439 @@ +/* mbed Library - ADC + * Copyright (c) 2010, sblandford + * released under MIT license http://mbed.org/licence/mit + */ +#include "mbed.h" +#include "adc.h" + + +ADC *ADC::instance; + +ADC::ADC(int sample_rate, int cclk_div) + { + + int i, adc_clk_freq, pclk, clock_div, max_div=1; + + //Work out CCLK + adc_clk_freq=CLKS_PER_SAMPLE*sample_rate; + int m = (LPC_SC->PLL0CFG & 0xFFFF) + 1; + int n = (LPC_SC->PLL0CFG >> 16) + 1; + int cclkdiv = LPC_SC->CCLKCFG + 1; + int Fcco = (2 * m * XTAL_FREQ) / n; + int cclk = Fcco / cclkdiv; + + //Power up the ADC + LPC_SC->PCONP |= (1 << 12); + //Set clock at cclk / 1. + LPC_SC->PCLKSEL0 &= ~(0x3 << 24); + switch (cclk_div) { + case 1: + LPC_SC->PCLKSEL0 |= 0x1 << 24; + break; + case 2: + LPC_SC->PCLKSEL0 |= 0x2 << 24; + break; + case 4: + LPC_SC->PCLKSEL0 |= 0x0 << 24; + break; + case 8: + LPC_SC->PCLKSEL0 |= 0x3 << 24; + break; + default: + printf("Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n", + cclk_div); + printf("Defaulting to 1.\n"); + LPC_SC->PCLKSEL0 |= 0x1 << 24; + break; + } + pclk = cclk / cclk_div; + clock_div=pclk / adc_clk_freq; + + if (clock_div > 0xFF) { + //printf("Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n", + // clock_div); + clock_div=0xFF; + } + if (clock_div == 0) { + printf("Warning: Clock division is 0. Re-Setting to 1.\n"); + clock_div=1; + } + + _adc_clk_freq=pclk / clock_div; + if (_adc_clk_freq > MAX_ADC_CLOCK) { + printf("Warning: Actual ADC sample rate of %u which is above %u limit\n", + _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE); + while ((pclk / max_div) > MAX_ADC_CLOCK) max_div++; + printf("Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE); + } + + LPC_ADC->ADCR = + ((clock_div - 1 ) << 8 ) | //Clkdiv + ( 1 << 21 ); //A/D operational + + //Default no channels enabled + LPC_ADC->ADCR &= ~0xFF; + //Default NULL global custom isr + _adc_g_isr = NULL; + //Initialize arrays + for (i=7; i>=0; i--) { + _adc_data[i] = 0; + _adc_isr[i] = NULL; + } + + + //* Attach IRQ + instance = this; + NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); + + //Disable global interrupt + LPC_ADC->ADINTEN &= ~0x100; + +}; + +void ADC::_adcisr(void) +{ + instance->adcisr(); +} + + +void ADC::adcisr(void) +{ + uint32_t stat; + int chan; + + // Read status + stat = LPC_ADC->ADSTAT; + //Scan channels for over-run or done and update array + if (stat & 0x0101) _adc_data[0] = LPC_ADC->ADDR0; + if (stat & 0x0202) _adc_data[1] = LPC_ADC->ADDR1; + if (stat & 0x0404) _adc_data[2] = LPC_ADC->ADDR2; + if (stat & 0x0808) _adc_data[3] = LPC_ADC->ADDR3; + if (stat & 0x1010) _adc_data[4] = LPC_ADC->ADDR4; + if (stat & 0x2020) _adc_data[5] = LPC_ADC->ADDR5; + if (stat & 0x4040) _adc_data[6] = LPC_ADC->ADDR6; + if (stat & 0x8080) _adc_data[7] = LPC_ADC->ADDR7; + + // Channel that triggered interrupt + chan = (LPC_ADC->ADGDR >> 24) & 0x07; + //User defined interrupt handlers + if (_adc_isr[chan] != NULL) + _adc_isr[chan](_adc_data[chan]); + if (_adc_g_isr != NULL) + _adc_g_isr(chan, _adc_data[chan]); + return; +} + +int ADC::_pin_to_channel(PinName pin) { + int chan; + switch (pin) { + case p15://=p0.23 of LPC1768 + default: + chan=0; + break; + case p16://=p0.24 of LPC1768 + chan=1; + break; + case p17://=p0.25 of LPC1768 + chan=2; + break; + case p18://=p0.26 of LPC1768 + chan=3; + break; + case p19://=p1.30 of LPC1768 + chan=4; + break; + case p20://=p1.31 of LPC1768 + chan=5; + break; + } + return(chan); +} + +PinName ADC::channel_to_pin(int chan) { + const PinName pin[8]={p15, p16, p17, p18, p19, p20, p15, p15}; + + if ((chan < 0) || (chan > 5)) + fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); + return(pin[chan & 0x07]); +} + + +int ADC::channel_to_pin_number(int chan) { + const int pin[8]={15, 16, 17, 18, 19, 20, 0, 0}; + + if ((chan < 0) || (chan > 5)) + fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); + return(pin[chan & 0x07]); +} + + +uint32_t ADC::_data_of_pin(PinName pin) { + //If in burst mode and at least one interrupt enabled then + //take all values from _adc_data + if (burst() && (LPC_ADC->ADINTEN & 0x3F)) { + return(_adc_data[_pin_to_channel(pin)]); + } else { + //Return current register value or last value from interrupt + switch (pin) { + case p15://=p0.23 of LPC1768 + default: + return(LPC_ADC->ADINTEN & 0x01?_adc_data[0]:LPC_ADC->ADDR0); + case p16://=p0.24 of LPC1768 + return(LPC_ADC->ADINTEN & 0x02?_adc_data[1]:LPC_ADC->ADDR1); + case p17://=p0.25 of LPC1768 + return(LPC_ADC->ADINTEN & 0x04?_adc_data[2]:LPC_ADC->ADDR2); + case p18://=p0.26 of LPC1768: + return(LPC_ADC->ADINTEN & 0x08?_adc_data[3]:LPC_ADC->ADDR3); + case p19://=p1.30 of LPC1768 + return(LPC_ADC->ADINTEN & 0x10?_adc_data[4]:LPC_ADC->ADDR4); + case p20://=p1.31 of LPC1768 + return(LPC_ADC->ADINTEN & 0x20?_adc_data[5]:LPC_ADC->ADDR5); + } + } +} + +//Enable or disable an ADC pin +void ADC::setup(PinName pin, int state) { + int chan; + chan=_pin_to_channel(pin); + if ((state & 1) == 1) { + switch(pin) { + case p15://=p0.23 of LPC1768 + default: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14; + break; + case p16://=p0.24 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16; + break; + case p17://=p0.25 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18; + break; + case p18://=p0.26 of LPC1768: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20; + break; + case p19://=p1.30 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28); + LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28; + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28); + LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28; + break; + case p20://=p1.31 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30); + LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30; + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30); + LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30; + break; + } + //Only one channel can be selected at a time if not in burst mode + if (!burst()) LPC_ADC->ADCR &= ~0xFF; + //Select channel + LPC_ADC->ADCR |= (1 << chan); + } + else { + switch(pin) { + case p15://=p0.23 of LPC1768 + default: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14); + break; + case p16://=p0.24 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16); + break; + case p17://=p0.25 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18); + break; + case p18://=p0.26 of LPC1768: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20); + break; + case p19://=p1.30 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28); + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28); + break; + case p20://=p1.31 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30); + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30); + break; + } + LPC_ADC->ADCR &= ~(1 << chan); + } +} +//Return channel enabled/disabled state +int ADC::setup(PinName pin) { + int chan; + + chan = _pin_to_channel(pin); + return((LPC_ADC->ADCR & (1 << chan)) >> chan); +} + +//Select channel already setup +void ADC::select(PinName pin) { + int chan; + + //Only one channel can be selected at a time if not in burst mode + if (!burst()) LPC_ADC->ADCR &= ~0xFF; + //Select channel + chan = _pin_to_channel(pin); + LPC_ADC->ADCR |= (1 << chan); +} + +//Enable or disable burst mode +void ADC::burst(int state) { + if ((state & 1) == 1) { + if (startmode(0) != 0) + fprintf(stderr, "Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0)); + LPC_ADC->ADCR |= (1 << 16); + } + else + LPC_ADC->ADCR &= ~(1 << 16); +} +//Return burst mode state +int ADC::burst(void) { + return((LPC_ADC->ADCR & (1 << 16)) >> 16); +} + +//Set startmode and edge +void ADC::startmode(int mode, int edge) { + int lpc_adc_temp; + + //Reset start mode and edge bit, + lpc_adc_temp = LPC_ADC->ADCR & ~(0x0F << 24); + //Write with new values + lpc_adc_temp |= ((mode & 7) << 24) | ((edge & 1) << 27); + LPC_ADC->ADCR = lpc_adc_temp; +} + +//Return startmode state according to mode_edge=0: mode and mode_edge=1: edge +int ADC::startmode(int mode_edge){ + switch (mode_edge) { + case 0: + default: + return((LPC_ADC->ADCR >> 24) & 0x07); + case 1: + return((LPC_ADC->ADCR >> 27) & 0x01); + } +} + +//Start ADC conversion +void ADC::start(void) { + startmode(1,0); +} + + +//Set interrupt enable/disable for pin to state +void ADC::interrupt_state(PinName pin, int state) { + int chan; + + chan = _pin_to_channel(pin); + if (state == 1) { + LPC_ADC->ADINTEN &= ~0x100; + LPC_ADC->ADINTEN |= 1 << chan; + /* Enable the ADC Interrupt */ + NVIC_EnableIRQ(ADC_IRQn); + } else { + LPC_ADC->ADINTEN &= ~( 1 << chan ); + //Disable interrrupt if no active pins left + if ((LPC_ADC->ADINTEN & 0xFF) == 0) + NVIC_DisableIRQ(ADC_IRQn); + } +} + +//Return enable/disable state of interrupt for pin +int ADC::interrupt_state(PinName pin) { + int chan; + + chan = _pin_to_channel(pin); + return((LPC_ADC->ADINTEN >> chan) & 0x01); +} + + +//Attach custom interrupt handler replacing default +void ADC::attach(void(*fptr)(void)) { + //* Attach IRQ + NVIC_SetVector(ADC_IRQn, (uint32_t)fptr); +} + +//Restore default interrupt handler +void ADC::detach(void) { + //* Attach IRQ + instance = this; + NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); +} + + +//Append interrupt handler for pin to function isr +void ADC::append(PinName pin, void(*fptr)(uint32_t value)) { + int chan; + + chan = _pin_to_channel(pin); + _adc_isr[chan] = fptr; +} + +//Append interrupt handler for pin to function isr +void ADC::unappend(PinName pin) { + int chan; + + chan = _pin_to_channel(pin); + _adc_isr[chan] = NULL; +} + +//Unappend global interrupt handler to function isr +void ADC::append(void(*fptr)(int chan, uint32_t value)) { + _adc_g_isr = fptr; +} + +//Detach global interrupt handler to function isr +void ADC::unappend() { + _adc_g_isr = NULL; +} + +//Set ADC offset +void offset(int offset) { + LPC_ADC->ADTRM &= ~(0x07 << 4); + LPC_ADC->ADTRM |= (offset & 0x07) << 4; +} + +//Return current ADC offset +int offset(void) { + return((LPC_ADC->ADTRM >> 4) & 0x07); +} + +//Return value of ADC on pin +int ADC::read(PinName pin) { + //Reset DONE and OVERRUN flags of interrupt handled ADC data + _adc_data[_pin_to_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30)); + //Return value + return((_data_of_pin(pin) >> 4) & 0xFFF); +} + +//Return DONE flag of ADC on pin +int ADC::done(PinName pin) { + return((_data_of_pin(pin) >> 31) & 0x01); +} + +//Return OVERRUN flag of ADC on pin +int ADC::overrun(PinName pin) { + return((_data_of_pin(pin) >> 30) & 0x01); +} + +int ADC::actual_adc_clock(void) { + return(_adc_clk_freq); +} + +int ADC::actual_sample_rate(void) { + return(_adc_clk_freq / CLKS_PER_SAMPLE); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ADC/adc.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,131 @@ +/* mbed Library - ADC + * Copyright (c) 2010, sblandford + * released under MIT license http://mbed.org/licence/mit + */ + +#ifndef MBED_ADC_H +#define MBED_ADC_H + +#include "PinNames.h" // mbed.h lib +#define XTAL_FREQ 12000000 +#define MAX_ADC_CLOCK 13000000 +#define CLKS_PER_SAMPLE 64 + +class ADC { +public: + + //Initialize ADC with ADC maximum sample rate of + //sample_rate and system clock divider of cclk_div + //Maximum recommened sample rate is 184000 + ADC(int sample_rate, int cclk_div); + + //Enable/disable ADC on pin according to state + //and also select/de-select for next conversion + void setup(PinName pin, int state); + + //Return enabled/disabled state of ADC on pin + int setup(PinName pin); + + //Enable/disable burst mode according to state + void burst(int state); + + //Select channel already setup + void select(PinName pin); + + //Return burst mode enabled/disabled + int burst(void); + + /*Set start condition and edge according to mode: + 0 - No start (this value should be used when clearing PDN to 0). + 1 - Start conversion now. + 2 - Start conversion when the edge selected by bit 27 occurs on the P2.10 / EINT0 / NMI pin. + 3 - Start conversion when the edge selected by bit 27 occurs on the P1.27 / CLKOUT / + USB_OVRCRn / CAP0.1 pin. + 4 - Start conversion when the edge selected by bit 27 occurs on MAT0.1. Note that this does + not require that the MAT0.1 function appear on a device pin. + 5 - Start conversion when the edge selected by bit 27 occurs on MAT0.3. Note that it is not + possible to cause the MAT0.3 function to appear on a device pin. + 6 - Start conversion when the edge selected by bit 27 occurs on MAT1.0. Note that this does + not require that the MAT1.0 function appear on a device pin. + 7 - Start conversion when the edge selected by bit 27 occurs on MAT1.1. Note that this does + not require that the MAT1.1 function appear on a device pin. + When mode >= 2, conversion is triggered by edge: + 0 - Rising edge + 1 - Falling edge + */ + void startmode(int mode, int edge); + + //Return startmode state according to mode_edge=0: mode and mode_edge=1: edge + int startmode(int mode_edge); + + //Start ADC conversion + void start(void); + + //Set interrupt enable/disable for pin to state + void interrupt_state(PinName pin, int state); + + //Return enable/disable state of interrupt for pin + int interrupt_state(PinName pin); + + //Attach custom interrupt handler replacing default + void attach(void(*fptr)(void)); + + //Restore default interrupt handler + void detach(void); + + //Append custom interrupt handler for pin + void append(PinName pin, void(*fptr)(uint32_t value)); + + //Unappend custom interrupt handler for pin + void unappend(PinName pin); + + //Append custom global interrupt handler + void append(void(*fptr)(int chan, uint32_t value)); + + //Unappend custom global interrupt handler + void unappend(void); + + //Set ADC offset to a value 0-7 + void offset(int offset); + + //Return current ADC offset + int offset(void); + + //Return value of ADC on pin + int read(PinName pin); + + //Return DONE flag of ADC on pin + int done(PinName pin); + + //Return OVERRUN flag of ADC on pin + int overrun(PinName pin); + + //Return actual ADC clock + int actual_adc_clock(void); + + //Return actual maximum sample rate + int actual_sample_rate(void); + + //Return pin ID of ADC channel + PinName channel_to_pin(int chan); + + //Return pin number of ADC channel + int channel_to_pin_number(int chan); + + +private: + int _pin_to_channel(PinName pin); + uint32_t _data_of_pin(PinName pin); + + int _adc_clk_freq; + void adcisr(void); + static void _adcisr(void); + static ADC *instance; + + uint32_t _adc_data[8]; + void(*_adc_isr[8])(uint32_t value); + void(*_adc_g_isr)(int chan, uint32_t value); + void(*_adc_m_isr)(void); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Adc.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,51 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "Adc.h" +#include "libs/ADC/adc.h" +#include "libs/Pin.h" + +Adc::Adc(){ + this->adc = new ADC(1000, 1); +} + +void Adc::enable_pin(Pin* pin){ + PinName pin_name = this->_pin_to_pinname(pin); + this->adc->burst(1); + this->adc->setup(pin_name,1); + this->adc->interrupt_state(pin_name,1); +} + +unsigned int Adc::read(Pin* pin){ + return this->adc->read(this->_pin_to_pinname(pin)); +} + +PinName Adc::_pin_to_pinname(Pin* pin){ + if( pin->port == LPC_GPIO0 && pin->pin == 23 ){ + return p15; + }else if( pin->port == LPC_GPIO0 && pin->pin == 24 ){ + return p16; + }else if( pin->port == LPC_GPIO0 && pin->pin == 25 ){ + return p17; + }else if( pin->port == LPC_GPIO0 && pin->pin == 26 ){ + return p18; + }else if( pin->port == LPC_GPIO1 && pin->pin == 30 ){ + return p19; + }else if( pin->port == LPC_GPIO1 && pin->pin == 31 ){ + return p20; + }else{ + //TODO: Error + error("Bad adc pin"); + + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Adc.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,34 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + + +#ifndef ADC_H +#define ADC_H + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "PinNames.h" // mbed.h lib +#include "libs/ADC/adc.h" +#include "libs/Pin.h" + +class Adc : public Module{ + public: + Adc(); + void enable_pin(Pin* pin); + unsigned int read(Pin* pin); + PinName _pin_to_pinname(Pin* pin); + + ADC* adc; +}; + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Config.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,150 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +using namespace std; +#include <vector> +#include <string> + +#include "libs/Kernel.h" +#include "Config.h" +#include "ConfigValue.h" +#include "ConfigSource.h" +#include "ConfigCache.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/SerialMessage.h" +#include "libs/ConfigSources/FileConfigSource.h" + + +Config::Config(){ + + this->config_cache_loaded = false; + + // Config source for */config files + this->config_sources.push_back( new FileConfigSource("/local/config", LOCAL_CONFIGSOURCE_CHECKSUM) ); + //this->config_sources.push_back( new FileConfigSource("/sd/config", SD_CONFIGSOURCE_CHECKSUM ) ); + + // Pre-load the config cache + this->config_cache_load(); + +} + +void Config::on_module_loaded(){} + +void Config::on_console_line_received( void* argument ){} + +void Config::set_string( string setting, string value ){ + ConfigValue* cv = new ConfigValue; + cv->found = true; + cv->check_sums = get_checksums(setting); + cv->value = value; + + this->config_cache.replace_or_push_back(cv); + + this->kernel->call_event(ON_CONFIG_RELOAD); +} + +void Config::get_module_list(vector<uint16_t>* list, uint16_t family){ + for( int i=1; i<this->config_cache.size(); i++){ + ConfigValue* value = this->config_cache.at(i); + if( value->check_sums.size() == 3 && value->check_sums.at(2) == 29545 && value->check_sums.at(0) == family ){ + // We found a module enable for this family, add it's number + list->push_back(value->check_sums.at(1)); + } + } +} + + +// Command to load config cache into buffer for multiple reads during init +void Config::config_cache_load(){ + + this->config_cache_clear(); + + // First element is a special empty ConfigValue for values not found + ConfigValue* result = new ConfigValue; + this->config_cache.push_back(result); + // For each ConfigSource in our stack + for( unsigned int i = 0; i < this->config_sources.size(); i++ ){ + ConfigSource* source = this->config_sources[i]; + source->transfer_values_to_cache(&this->config_cache); + } + + this->config_cache_loaded = true; +} + +// Command to clear the config cache after init +void Config::config_cache_clear(){ + while( ! this->config_cache.empty() ){ + delete this->config_cache.back(); + this->config_cache.pop_back(); + } + this->config_cache_loaded = false; +} + + +ConfigValue* Config::value(uint16_t check_sum_a, uint16_t check_sum_b, uint16_t check_sum_c ){ + vector<uint16_t> check_sums; + check_sums.push_back(check_sum_a); + check_sums.push_back(check_sum_b); + check_sums.push_back(check_sum_c); + return this->value(check_sums); +} + +ConfigValue* Config::value(uint16_t check_sum_a, uint16_t check_sum_b){ + vector<uint16_t> check_sums; + check_sums.push_back(check_sum_a); + check_sums.push_back(check_sum_b); + return this->value(check_sums); +} + +ConfigValue* Config::value(uint16_t check_sum){ + vector<uint16_t> check_sums; + check_sums.push_back(check_sum); + return this->value(check_sums); +} + +// Get a value from the configuration as a string +// Because we don't like to waste space in Flash with lengthy config parameter names, we take a checksum instead so that the name does not have to be stored +// See get_checksum +ConfigValue* Config::value(vector<uint16_t> check_sums){ + ConfigValue* result = this->config_cache[0]; + //if( this->has_config_file() == false ){ + // return result; + //} + // Check if the config is cached, and load it temporarily if it isn't + bool cache_preloaded = this->config_cache_loaded; + if( !cache_preloaded ){ this->config_cache_load(); } + + for( int i=1; i<this->config_cache.size(); i++){ + // If this line matches the checksum + bool match = true; + for( unsigned int j = 0; j < check_sums.size(); j++ ){ + uint16_t checksum_node = check_sums[j]; + + //printf("%u(%s) against %u\r\n", get_checksum(key_node), key_node.c_str(), checksum_node); + if(this->config_cache[i]->check_sums[j] != checksum_node ){ + match = false; + break; + } + } + if( match == false ){ + //printf("continue\r\n"); + continue; + } + result = this->config_cache[i]; + break; + } + + if( !cache_preloaded ){ + this->config_cache_clear(); + } + return result; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Config.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,53 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef CONFIG_H +#define CONFIG_H +#include "libs/Kernel.h" +#include "libs/utils.h" +#include "libs/Pin.h" +#include "ConfigValue.h" +#include "ConfigCache.h" +#include "ConfigSource.h" +#include "libs/ConfigSources/FileConfigSource.h" + +#define error(...) (fprintf(stderr, __VA_ARGS__), exit(1)) + +using namespace std; +#include <vector> +#include <string> +#include <stdio.h> + +#define LOCAL_CONFIGSOURCE_CHECKSUM 13581 +#define SD_CONFIGSOURCE_CHECKSUM 19415 + + +class Config : public Module { + public: + Config(); + + virtual void on_module_loaded(); + virtual void on_console_line_received( void* argument ); + void config_cache_load(); + void config_cache_clear(); + void set_string( string setting , string value); + + ConfigValue* value(uint16_t check_sum); + ConfigValue* value(uint16_t check_sum_a, uint16_t check_sum_b); + ConfigValue* value(uint16_t check_sum_a, uint16_t check_sum_b, uint16_t check_sum_c ); + ConfigValue* value(vector<uint16_t> check_sums ); + + void get_module_list(vector<uint16_t>* list, uint16_t family); + + bool has_characters(uint16_t check_sum, string str ); + + ConfigCache config_cache; // A cache in which ConfigValues are kept + vector<ConfigSource*> config_sources; // A list of all possible coniguration sources + bool config_cache_loaded; // Whether or not the cache is currently popluated +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ConfigCache.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,54 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef CONFIGCACHE_H +#define CONFIGCACHE_H + +using namespace std; +#include <vector> + +#include "ConfigValue.h" + +class ConfigCache : public std::vector<ConfigValue*> { + public: + ConfigCache(){} + + // If we find an existing value, replace it, otherwise, push it at the back of the list + void replace_or_push_back(ConfigValue* new_value){ + + bool value_exists = false; + // For each already existing element + for( int i=1; i<this->size(); i++){ + // If this configvalue matches the checksum + bool match = true; + for( unsigned int j = 0; j < new_value->check_sums.size(); j++ ){ + uint16_t checksum_node = new_value->check_sums[j]; + if(this->at(i)->check_sums[j] != checksum_node ){ + match = false; + break; + } + } + if( match == false ){ continue; } + value_exists = true; + // Replace with the provided value + delete this->at(i); + this->at(i) = new_value; + break; + } + + // Value does not already exists, add to the list + if( value_exists == false ){ + this->push_back(new_value); + } + + } + +}; + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ConfigSource.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,34 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef CONFIGSOURCE_H +#define CONFIGSOURCE_H + +using namespace std; +#include <vector> +#include <string> +#include "ConfigValue.h" +#include "ConfigCache.h" + +class ConfigValue; + +class ConfigSource { + public: + ConfigSource(){} + + // Read each value, and append it as a ConfigValue to the config_cache we were passed + virtual void transfer_values_to_cache( ConfigCache* ){} + virtual bool is_named( uint16_t check_sum ){return false;} + virtual void write( string setting, string value ){} + virtual string read( vector<uint16_t> check_sums ){return "";} + + uint16_t name_checksum; +}; + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ConfigSources/FileConfigSource.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,177 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Kernel.h" +#include "ConfigValue.h" +#include "FileConfigSource.h" +#include "ConfigCache.h" +using namespace std; +#include <string> + +FileConfigSource::FileConfigSource(string config_file, uint16_t name_checksum){ + this->name_checksum = name_checksum; + this->config_file = config_file; + this->config_file_found = false; +} + +// Transfer all values found in the file to the passed cache +void FileConfigSource::transfer_values_to_cache( ConfigCache* cache ){ + // Default empty value + ConfigValue* result = new ConfigValue; + if( this->has_config_file() == false ){ + return; + } + // Open the config file ( find it if we haven't already found it ) + FILE *lp = fopen(this->get_config_file().c_str(), "r"); + string buffer; + int c; + // For each line + do { + c = fgetc (lp); + if (c == '\n' || c == EOF){ + // We have a new line + if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments + if( buffer.length() < 3 ){ buffer.clear(); continue; } //Ignore empty lines + size_t begin_key = buffer.find_first_not_of(" "); + size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key)); + string key = buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key).append(" "); + vector<uint16_t> check_sums = get_checksums(key); + + result = new ConfigValue; + result->found = true; + result->check_sums = check_sums; + result->value = buffer.substr(begin_value, buffer.find_first_of("\r\n# ", begin_value+1)-begin_value); + + // Append the newly found value to the cache we were passed + cache->replace_or_push_back(result); + + buffer.clear(); + }else{ + buffer += c; + } + } while (c != EOF); + fclose(lp); + +} + +// Return true if the check_sums match +bool FileConfigSource::is_named( uint16_t check_sum ){ + return check_sum == this->name_checksum; +} + +// Write a config setting to the file +void FileConfigSource::write( string setting, string value ){ +/* + // Open the config file ( find it if we haven't already found it ) + FILE *lp = fopen(this->get_config_file().c_str(), "r+"); + string buffer; + int c; + // For each line + do { + c = fgetc (lp); + if (c == '\n' || c == EOF){ + // We have a new line + if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments + if( buffer.length() < 3 ){ buffer.clear(); continue; } //Ignore empty lines + size_t begin_key = buffer.find_first_not_of(" "); + size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key)); + // If this line matches the checksum + string candidate = buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key); + if( candidate.compare(setting) != 0 ){ buffer.clear(); continue; } + int free_space = int(int(buffer.find_first_of("\r\n#", begin_value+1))-begin_value); + if( int(value.length()) >= free_space ){ + //this->kernel->serial->printf("ERROR: Not enough room for value\r\n"); + fclose(lp); + return; + } + // Update value + for( int i = value.length(); i < free_space; i++){ value += " "; } + fpos_t pos; + fgetpos( lp, &pos ); + int start = pos - buffer.length() + begin_value - 1; + fseek(lp, start, SEEK_SET); + fputs(value.c_str(), lp); + fclose(lp); + return; + }else{ + buffer += c; + } + } while (c != EOF); + fclose(lp); + //this->kernel->serial->printf("ERROR: configuration key not found\r\n"); + */ +} + +// Return the value for a specific checksum +string FileConfigSource::read( vector<uint16_t> check_sums ){ + + string value = ""; + + if( this->has_config_file() == false ){return value;} + // Open the config file ( find it if we haven't already found it ) + FILE *lp = fopen(this->get_config_file().c_str(), "r"); + string buffer; + int c; + // For each line + do { + c = fgetc (lp); + if (c == '\n' || c == EOF){ + // We have a new line + if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments + if( buffer.length() < 3 ){ buffer.clear(); continue; } //Ignore empty lines + size_t begin_key = buffer.find_first_not_of(" "); + size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key)); + string key = buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key).append(" "); + vector<uint16_t> line_checksums = get_checksums(key); + + if(check_sums == line_checksums){ + value = buffer.substr(begin_value, buffer.find_first_of("\r\n# ", begin_value+1)-begin_value); + break; + } + + buffer.clear(); + }else{ + buffer += c; + } + } while (c != EOF); + fclose(lp); + + return value; +} + +// Return wether or not we have a readable config file +bool FileConfigSource::has_config_file(){ + if( this->config_file_found ){ return true; } + this->try_config_file(this->config_file); + if( this->config_file_found ){ + return true; + }else{ + return false; + } + +} + +// Tool function for get_config_file +inline void FileConfigSource::try_config_file(string candidate){ + if(file_exists(candidate)){ + this->config_file_found = true; + } +} + +// Get the filename for the config file +string FileConfigSource::get_config_file(){ + if( this->config_file_found ){ return this->config_file; } + if( this->has_config_file() ){ + return this->config_file; + }else{ + printf("ERROR: no config file found\r\n"); + } +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ConfigSources/FileConfigSource.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,39 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef FILECONFIGSOURCE_H +#define FILECONFIGSOURCE_H + +#include "ConfigValue.h" +#include "ConfigSource.h" +#include "ConfigCache.h" + +using namespace std; +#include <string> + +#define FILE_CONFIGSOURCE_CHECKSUM 5281 // "file" + +class FileConfigSource : public ConfigSource { + public: + FileConfigSource(string config_file = "/sd/config", uint16_t name_checksum = FILE_CONFIGSOURCE_CHECKSUM); + virtual void transfer_values_to_cache( ConfigCache* cache ); + virtual bool is_named( uint16_t check_sum ); + virtual void write( string setting, string value ); + virtual string read( vector<uint16_t> check_sums ); + bool has_config_file(); + void try_config_file(string candidate); + string get_config_file(); + + string config_file; // Path to the config file + bool config_file_found; // Wether or not the config file's location is known + + +}; + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ConfigValue.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,115 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef CONFIGVALUE_H +#define CONFIGVALUE_H + +#include "libs/Kernel.h" +#include "libs/utils.h" +#include "libs/Pin.h" + + +#define error(...) (fprintf(stderr, __VA_ARGS__), exit(1)) + +using namespace std; +#include <vector> +#include <string> +#include <stdio.h> + + +class ConfigValue{ + public: + ConfigValue(){ + this->found = false; + this->default_set = false; + }; + + ConfigValue* required(){ + if( this->found == true ){ + return this; + }else{ + error("could not find config setting with checksum %u, please see http://smoothieware.org/configuring-smoothie\r\n", this->check_sum ); + return NULL; + } + } + + double as_number(){ + if( this->found == false && this->default_set == true ){ + return this->default_double; + }else{ + double result = atof(remove_non_number(this->value).c_str()); + if( result == 0.0 && this->value.find_first_not_of("0.") != string::npos ){ + error("config setting with value '%s' is not a valid number, please see http://smoothieware.org/configuring-smoothie\r\n", this->value.c_str() ); + } + return result; + } + + } + + std::string as_string(){ + if( this->found == false && this->default_set == true ){ + return this->default_string; + }else{ + return this->value; + } + } + + bool as_bool(){ + if( this->found == false && this->default_set == true ){ + return this->default_double; + }else{ + if( this->value.find_first_of("t1") != string::npos ){ + return true; + }else{ + return false; + } + } + } + + Pin* as_pin(){ + Pin* pin = new Pin(); + pin->from_string(this->as_string()); + return pin; + } + + ConfigValue* by_default(double val){ + this->default_set = true; + this->default_double = val; + return this; + } + + ConfigValue* by_default(std::string val){ + this->default_set = true; + this->default_string = val; + return this; + } + + bool has_characters( string mask ){ + if( this->value.find_first_of(mask) != string::npos ){ return true; }else{ return false; } + } + + bool is_inverted(){ + return this->has_characters(string("!")); + } + + string value; + vector<uint16_t> check_sums; + uint16_t check_sum; + bool found; + bool default_set; + double default_double; + string default_string; +}; + + + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Digipot.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,47 @@ +#ifndef DIGIPOT_H +#define DIGIPOT_H + +#include "libs/Kernel.h" +#include "I2C.h" // mbed.h lib +#include "libs/utils.h" +#include <string> +#include <math.h> + +class Digipot{ + public: + Digipot(){ } + + char current_to_wiper( double current ){ + return char(ceil(double((113.33*current)))); + } + + void i2c_send( char first, char second, char third ){ + this->i2c->start(); + this->i2c->write(first); + this->i2c->write(second); + this->i2c->write(third); + this->i2c->stop(); + } + + void set_current( int channel, double current ){ + + current = min( max( current, 0.0L ), 2.0L ); + + // I2C com + this->i2c = new mbed::I2C(p9, p10); + + // Initial setup + this->i2c_send( 0x58, 0x40, 0xff ); + this->i2c_send( 0x58, 0xA0, 0xff ); + + // Set actual wiper value + char adresses[4] = { 0x00, 0x10, 0x60, 0x70 }; + this->i2c_send( 0x58, adresses[channel], this->current_to_wiper(current) ); + + } + + mbed::I2C* i2c; +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/FPointer.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,162 @@ +/* +Copyright (c) 2011 Andy Kirkham +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 AJK_FPOINTER_H +#define AJK_FPOINTER_H +#ifndef NULL +#define NULL 0 +#endif +namespace AjK { + +class FPointerDummy; + +/** FPointer - Adds callbacks that take and return a 32bit uint32_t data type. +* +* The Mbed library supplies a callback using the FunctionPointer object as +* defined in FunctionPointer.h However, this callback system does not allow +* the caller to pass a value to the callback. Likewise, the callback itself +* cannot return a value. +* +* FPointer operates in the same way but allows the callback function to be +* passed one arg, a uint32_t value. Additionally, the callback can return +* a single uint32_t value. The reason for using uint32_t is that the Mbed +* and the microcontroller (LPC1768) have a natural data size of 32bits and +* this means we can use the uint32_t as a pointer. See example1.h for more +* information. This example passes an "int" by passing a pointer to that +* int as a 32bit value. Using this technique you can pass any value you like. +* All you have to do is pass a pointer to your value cast to (uint32_t). Your +* callback can the deference it to get the original value. +* +* example2.h shows how to do the same thing but demostrates how to specify +* the callback into a class object/method. +* +* Finally, example3.h shows how to pass multiple values. In this example we +* define a data structure and in the callback we pass a pointer to that +* data structure thus allowing the callback to again get the values. +* +* Note, when passing pointers to variables to the callback, if the callback +* function/method changes that variable's value then it will also change the +* value the caller sees. If C pointers are new to you, you are strongly +* advised to read up on the subject. It's pointers that often get beginners +* into trouble when mis-used. +* +* @see example1.h +* @see example2.h +* @see example3.h +* @see http://mbed.org/handbook/C-Data-Types +* @see http://mbed.org/projects/libraries/svn/mbed/trunk/FunctionPointer.h +*/ +class FPointer { + +protected: + + //! C callback function pointer. + uint32_t (*c_callback)(uint32_t); + + //! C++ callback object/method pointer (the object part). + FPointerDummy *obj_callback; + + //! C++ callback object/method pointer (the method part). + uint32_t (FPointerDummy::*method_callback)(uint32_t); + +public: + + /** Constructor +*/ + FPointer() { + c_callback = NULL; + obj_callback = NULL; + method_callback = NULL; + } + + /** attach - Overloaded attachment function. +* +* Attach a C type function pointer as the callback. +* +* Note, the callback function prototype must be:- +* @code +* uint32_t myCallbackFunction(uint32_t); +* @endcode +* @param A C function pointer to call. +*/ + void attach(uint32_t (*function)(uint32_t) = 0) { c_callback = function; } + + /** attach - Overloaded attachment function. +* +* Attach a C++ type object/method pointer as the callback. +* +* Note, the callback method prototype must be:- +* @code +* public: +* uint32_t myCallbackFunction(uint32_t); +* @endcode +* @param A C++ object pointer. +* @param A C++ method within the object to call. +*/ + template<class T> + void attach(T* item, uint32_t (T::*method)(uint32_t)) { + obj_callback = (FPointerDummy *)item; + method_callback = (uint32_t (FPointerDummy::*)(uint32_t))method; + } + + /** call - Overloaded callback initiator. +* +* call the callback function. +* +* @param uint32_t The value to pass to the callback. +* @return uint32_t The value the callback returns. +*/ + uint32_t call(uint32_t arg) { + if (c_callback != NULL) { + return (*c_callback)(arg); + } + else { + if (obj_callback != NULL && method_callback != NULL) { + return (obj_callback->*method_callback)(arg); + } + } + return (uint32_t)NULL; + } + + /** call - Overloaded callback initiator. +* +* Call the callback function without passing an argument. +* The callback itself is passed NULL. Note, the callback +* prototype should still be <b>uint32_t callback(uint32_t)</b>. +* +* @return uint32_t The value the callback returns. +*/ + uint32_t call(void) { + if (c_callback != NULL) { + return (*c_callback)((uint32_t)NULL); + } + else { + if (obj_callback != NULL && method_callback != NULL) { + return (obj_callback->*method_callback)((uint32_t)NULL); + } + } + return (uint32_t)NULL; + } +}; + +}; // namespace AjK ends + +using namespace AjK; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Hook.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,6 @@ +extern "C"{ + #include <stdint.h> +} +#include "Hook.h" + +Hook::Hook(){}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Hook.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,12 @@ +#ifndef HOOK_H +#define HOOK_H +#include "libs/FPointer.h" + +class Hook : public FPointer { + public: + Hook(); + double frequency; + double counter; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Kernel.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,97 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +using namespace std; +#include <vector> +#include "libs/Kernel.h" +#include "libs/Module.h" +#include "libs/Config.h" +#include "libs/nuts_bolts.h" +#include "libs/SlowTicker.h" +#include "libs/Adc.h" +#include "libs/Digipot.h" +#include "libs/Pauser.h" + +#include "modules/communication/SerialConsole.h" +#include "modules/communication/GcodeDispatch.h" +#include "modules/robot/Planner.h" +#include "modules/robot/Robot.h" +#include "modules/robot/Stepper.h" +#include "modules/robot/Player.h" + + +// List of callback functions, ordered as their corresponding events +const ModuleCallback kernel_callback_functions[NUMBER_OF_DEFINED_EVENTS] = { + &Module::on_main_loop, + &Module::on_console_line_received, + &Module::on_gcode_received, + &Module::on_stepper_wake_up, + &Module::on_gcode_execute, + &Module::on_speed_change, + &Module::on_block_begin, + &Module::on_block_end, + &Module::on_config_reload, + &Module::on_play, + &Module::on_pause, + &Module::on_idle +}; + +#define baud_rate_setting_checksum 10922 +#define uart0_checksum 16877 + +// The kernel is the central point in Smoothie : it stores modules, and handles event calls +Kernel::Kernel(){ + + // Config first, because we need the baud_rate setting before we start serial + this->config = new Config(); + + // Serial second, because the other modules might want to say something + this->serial = new SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(9600)->as_number()); + + this->add_module( this->config ); + this->add_module( this->serial ); + + // HAL stuff + this->slow_ticker = new SlowTicker(); + this->step_ticker = new StepTicker(); + this->adc = new Adc(); + this->digipot = new Digipot(); + + // LPC17xx-specific + NVIC_SetPriority(TIMER0_IRQn, 1); + NVIC_SetPriority(TIMER2_IRQn, 2); + + // Core modules + this->add_module( this->gcode_dispatch = new GcodeDispatch() ); + this->add_module( this->robot = new Robot() ); + this->add_module( this->stepper = new Stepper() ); + this->add_module( this->planner = new Planner() ); + this->add_module( this->player = new Player() ); + this->add_module( this->pauser = new Pauser() ); +} + +void Kernel::add_module(Module* module){ + module->kernel = this; + module->on_module_loaded(); + module->register_for_event(ON_CONFIG_RELOAD); +} + +void Kernel::register_for_event(unsigned int id_event, Module* module){ + this->hooks[id_event].push_back(module); +} + +void Kernel::call_event(unsigned int id_event){ + for(unsigned int i=0; i < this->hooks[id_event].size(); i++){ + (this->hooks[id_event][i]->*kernel_callback_functions[id_event])(this); + } +} + +void Kernel::call_event(unsigned int id_event, void * argument){ + for(unsigned int i=0; i < this->hooks[id_event].size(); i++){ + (this->hooks[id_event][i]->*kernel_callback_functions[id_event])(argument); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Kernel.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,77 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef KERNEL_H +#define KERNEL_H +#include "libs/Module.h" +#include "libs/Config.h" +#include "libs/SlowTicker.h" +#include "libs/StepTicker.h" +#include "libs/Adc.h" +#include "libs/Digipot.h" +#include "libs/Pauser.h" +#include "modules/communication/SerialConsole.h" +#include "modules/communication/GcodeDispatch.h" +#include "modules/robot/Planner.h" +#include "modules/robot/Robot.h" +#include "modules/robot/Stepper.h" + +// See : http://smoothieware.org/listofevents +#define NUMBER_OF_DEFINED_EVENTS 12 +#define ON_MAIN_LOOP 0 +#define ON_CONSOLE_LINE_RECEIVED 1 +#define ON_GCODE_RECEIVED 2 +#define ON_STEPPER_WAKE_UP 3 //TODO :Â Remove the need for this event, then this event itself eg:Â have planner call stepper directly +#define ON_GCODE_EXECUTE 4 +#define ON_SPEED_CHANGE 5 +#define ON_BLOCK_BEGIN 6 +#define ON_BLOCK_END 7 +#define ON_CONFIG_RELOAD 8 +#define ON_PLAY 9 +#define ON_PAUSE 10 +#define ON_IDLE 11 + + +using namespace std; +#include <vector> + +typedef void (Module::*ModuleCallback)(void * argument); + +//Module manager +class Module; +class Player; +class SlowTicker; +class Kernel { + public: + Kernel(); + void add_module(Module* module); + void register_for_event(unsigned int id_event, Module* module); + void call_event(unsigned int id_event); + void call_event(unsigned int id_event, void * argument); + + // These modules are aviable to all other modules + SerialConsole* serial; + GcodeDispatch* gcode_dispatch; + Robot* robot; + Stepper* stepper; + Planner* planner; + Config* config; + Player* player; + Pauser* pauser; + + int debug; + SlowTicker* slow_ticker; + StepTicker* step_ticker; + Adc* adc; + Digipot* digipot; + + private: + vector<Module*> hooks[NUMBER_OF_DEFINED_EVENTS]; // When a module asks to be called for a specific event ( a hook ), this is where that request is remembered + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Module.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,31 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" + +Module::Module(){ } + +void Module::on_module_loaded(){ +} + +void Module::register_for_event(int event_id){ + this->kernel->register_for_event(event_id, this); +} + +void Module::on_main_loop( void * argument){} +void Module::on_console_line_received( void * argument){} +void Module::on_gcode_received( void * argument){} +void Module::on_stepper_wake_up( void * argument){} +void Module::on_gcode_execute( void * argument){} +void Module::on_speed_change( void * argument){} +void Module::on_block_begin( void * argument){} +void Module::on_block_end( void * argument){} +void Module::on_config_reload( void * argument){} +void Module::on_play( void * argument){} +void Module::on_pause( void * argument){} +void Module::on_idle( void * argument){}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Module.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,37 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef MODULE_H +#define MODULE_H + +#include <string> +using std::string; + +// Module base class +// All modules must extend this class, see http://smoothieware.org/moduleexample +class Kernel; +class Module { + public: + Module(); + virtual void on_module_loaded(); + virtual void register_for_event(int event_id); + virtual void on_main_loop(void * argument); + virtual void on_console_line_received( void * argument); + virtual void on_gcode_received( void * argument); + virtual void on_gcode_execute( void * argument); + virtual void on_stepper_wake_up( void * argument); + virtual void on_speed_change( void * argument); + virtual void on_block_begin( void * argument); + virtual void on_block_end( void * argument); + virtual void on_config_reload( void * argument); + virtual void on_play( void * argument); + virtual void on_pause( void * argument); + virtual void on_idle( void * argument); + Kernel * kernel; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Pauser.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,26 @@ +#include "libs/Kernel.h" +#include "Pauser.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include <string> +using namespace std; + +Pauser::Pauser(){} + +void Pauser::on_module_loaded(){ + this->counter = 0; +} + +void Pauser::take(){ + this->counter++; + if( this->counter == 1 ){ + this->kernel->call_event(ON_PAUSE, &this->counter); + } +} + +void Pauser::release(){ + this->counter--; + if( this->counter == 0 ){ + this->kernel->call_event(ON_PLAY, &this->counter); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Pauser.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,18 @@ +#ifndef PAUSER_H +#define PAUSER_H + +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" + +class Pauser : public Module { + public: + Pauser(); + virtual void on_module_loaded(); + void take(); + void release(); + + unsigned short counter; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Pin.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,64 @@ +#ifndef PIN_H +#define PIN_H + +#include <stdlib.h> +#include "mbed.h" // smoothed mbed.h lib +#include "libs/Kernel.h" +#include "libs/utils.h" +#include <string> + +class Pin{ + public: + Pin(){ } + + Pin* from_string(std::string value){ + LPC_GPIO_TypeDef* gpios[5] ={LPC_GPIO0,LPC_GPIO1,LPC_GPIO2,LPC_GPIO3,LPC_GPIO4}; + this->port_number = atoi(value.substr(0,1).c_str()); + this->port = gpios[this->port_number]; + this->inverting = ( value.find_first_of("!")!=string::npos ? true : false ); + this->pin = atoi( value.substr(2, value.size()-2-(this->inverting?1:0)).c_str() ); + return this; + } + + inline Pin* as_output(){ + this->port->FIODIR |= 1<<this->pin; + return this; + } + + inline Pin* as_input(){ + this->port->FIODIR &= ~(1<<this->pin); + return this; + } + + inline Pin* as_open_drain(){ + if( this->port_number == 0 ){ LPC_PINCON->PINMODE_OD0 |= (1<<this->pin); } + if( this->port_number == 1 ){ LPC_PINCON->PINMODE_OD1 |= (1<<this->pin); } + if( this->port_number == 2 ){ LPC_PINCON->PINMODE_OD2 |= (1<<this->pin); } + if( this->port_number == 3 ){ LPC_PINCON->PINMODE_OD3 |= (1<<this->pin); } + if( this->port_number == 4 ){ LPC_PINCON->PINMODE_OD4 |= (1<<this->pin); } + return this; + } + + inline bool get(){ + return this->inverting ^ (( this->port->FIOPIN >> this->pin ) & 1); + } + + inline void set(bool value){ + value = this->inverting ^ value; + if( value ){ + this->port->FIOSET = 1 << this->pin; + }else{ + this->port->FIOCLR = 1 << this->pin; + } + } + + bool inverting; + LPC_GPIO_TypeDef* port; + char port_number; + char pin; +}; + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/RingBuffer.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,103 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. + + With chucks taken from http://en.wikipedia.org/wiki/Circular_buffer, see licence there also +*/ + +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + + +template<class kind, int length> class RingBuffer { + public: + RingBuffer(); + int size(); + int capacity(); + int next_block_index(int index); + int prev_block_index(int index); + void push_back(kind object); + void pop_front(kind &object); + void get( int index, kind &object); + kind* get_ref( int index); + void delete_first(); + + kind buffer[length]; + int head; + int tail; +}; + + +template<class kind, int length> RingBuffer<kind, length>::RingBuffer(){ + this->head = this->tail = 0; +} + +template<class kind, int length> int RingBuffer<kind, length>::capacity(){ + return length-1; +} + +template<class kind, int length> int RingBuffer<kind, length>::size(){ +return((this->head>this->tail)?length:0)+this->tail-head; +} + +template<class kind, int length> int RingBuffer<kind, length>::next_block_index(int index){ + index++; + if (index == length) { index = 0; } + return(index); +} + +template<class kind, int length> int RingBuffer<kind, length>::prev_block_index(int index){ + if (index == 0) { index = length; } + index--; + return(index); +} + +template<class kind, int length> void RingBuffer<kind, length>::push_back(kind object){ + this->buffer[this->tail] = object; + this->tail = (tail+1)&(length-1); +} + +template<class kind, int length> void RingBuffer<kind, length>::get(int index, kind &object){ + int j= 0; + int k= this->head; + while (k != this->tail){ + if (j == index) break; + j++; + k= (k + 1) & (length - 1); + } + if (k == this->tail){ + //return NULL; + } + object = this->buffer[k]; +} + + +template<class kind, int length> kind* RingBuffer<kind, length>::get_ref(int index){ + int j= 0; + int k= this->head; + while (k != this->tail){ + if (j == index) break; + j++; + k= (k + 1) & (length - 1); + } + if (k == this->tail){ + return NULL; + } + return &(this->buffer[k]); +} + +template<class kind, int length> void RingBuffer<kind, length>::pop_front(kind &object){ + object = this->buffer[this->head]; + this->head = (this->head+1)&(length-1); +} + +template<class kind, int length> void RingBuffer<kind, length>::delete_first(){ + //kind dummy; + //this->pop_front(dummy); + this->head = (this->head+1)&(length-1); +} + + +#endif
Binary file libs/RingBuffer.o has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/SerialMessage.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,8 @@ +#ifndef SERIALMESSAGE_H +#define SERIALMESSAGE_H +#include "libs/StreamOutput.h" +struct SerialMessage { + StreamOutput* stream; + std::string message; +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/SlowTicker.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,53 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "SlowTicker.h" +#include "libs/Hook.h" +#include "system_LPC17xx.h" // mbed.h lib + + +SlowTicker* global_slow_ticker; + +SlowTicker::SlowTicker(){ + this->max_frequency = 1; + global_slow_ticker = this; + LPC_SC->PCONP |= (1 << 22); // Power Ticker ON + LPC_TIM2->MR0 = 10000; // Initial dummy value for Match Register + LPC_TIM2->MCR = 3; // Match on MR0, reset on MR0 + LPC_TIM2->TCR = 1; // Enable interrupt + NVIC_EnableIRQ(TIMER2_IRQn); // Enable interrupt handler +} + +void SlowTicker::set_frequency( int frequency ){ + LPC_TIM2->MR0 = (SystemCoreClock/4)/frequency; // SystemCoreClock/4 = Timer increments in a second + LPC_TIM2->TCR = 3; // Reset + LPC_TIM2->TCR = 1; // Reset +} + +void SlowTicker::tick(){ + for (int i=0; i<this->hooks.size(); i++){ + Hook* hook = this->hooks.at(i); + hook->counter += ( hook->frequency / this->max_frequency ); + if( hook->counter > 0 ){ + hook->counter-=1; + hook->call(); + } + } +} + +extern "C" void TIMER2_IRQHandler (void){ + if((LPC_TIM2->IR >> 0) & 1){ // If interrupt register set for MR0 + LPC_TIM2->IR |= 1 << 0; // Reset it + global_slow_ticker->tick(); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/SlowTicker.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,46 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + + +#ifndef SLOWTICKER_H +#define SLOWTICKER_H + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "libs/Hook.h" + +class SlowTicker : public Module{ + public: + SlowTicker(); + void set_frequency( int frequency ); + void tick(); + // For some reason this can't go in the .cpp, see : http://mbed.org/forum/mbed/topic/2774/?page=1#comment-14221 + template<typename T> void attach( double frequency, T *optr, uint32_t ( T::*fptr )( uint32_t ) ){ + Hook* hook = new Hook(); + hook->frequency = frequency; + hook->attach(optr, fptr); + hook->counter = -2; + if( frequency > this->max_frequency ){ + this->max_frequency = frequency; + } + this->set_frequency(this->max_frequency); + this->hooks.push_back(hook); + } + + vector<Hook*> hooks; + double max_frequency; +}; + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/StepTicker.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,65 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "StepTicker.h" +#include "system_LPC17xx.h" // mbed.h lib + + +StepTicker* global_step_ticker; + +StepTicker::StepTicker(){ + global_step_ticker = this; + LPC_TIM0->MR0 = 1000000; // Initial dummy value for Match Register + LPC_TIM0->MCR = 11; // Match on MR0, reset on MR0, match on MR1 + LPC_TIM0->TCR = 1; // Enable interrupt + NVIC_EnableIRQ(TIMER0_IRQn); // Enable interrupt handler +} + +void StepTicker::set_frequency( double frequency ){ + this->frequency = frequency; + LPC_TIM0->MR0 = int(floor((SystemCoreClock/4)/frequency)); // SystemCoreClock/4Â = Timer increments in a second + if( LPC_TIM0->TC > LPC_TIM0->MR0 ){ + LPC_TIM0->TCR = 3; // Reset + LPC_TIM0->TCR = 1; // Reset + } +} + +void StepTicker::set_reset_delay( double seconds ){ + LPC_TIM0->MR1 = int(floor(double(SystemCoreClock/4)*( seconds ))); // SystemCoreClock/4Â = Timer increments in a second +} + +void StepTicker::tick(){ + for (int i=0; i<this->hooks.size(); i++){ + this->hooks.at(i)->call(); + } +} + +void StepTicker::reset_tick(){ + for (int i=0; i<this->reset_hooks.size(); i++){ + this->reset_hooks.at(i)->call(); + } +} + +extern "C" void TIMER0_IRQHandler (void){ + if((LPC_TIM0->IR >> 0) & 1){ // If interrupt register set for MR0 + LPC_TIM0->IR |= 1 << 0; // Reset it + global_step_ticker->tick(); + } + if((LPC_TIM0->IR >> 1) & 1){ // If interrupt register set for MR1 + LPC_TIM0->IR |= 1 << 1; // Reset it + global_step_ticker->reset_tick(); + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/StepTicker.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,58 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + + +#ifndef STEPTICKER_H +#define STEPTICKER_H + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/Module.h" +#include "libs/Kernel.h" + + +class StepTicker{ + public: + StepTicker(); + void set_frequency( double frequency ); + void tick(); + void set_reset_delay( double seconds ); + void reset_tick(); + + // For some reason this can't go in the .cpp, see : http://mbed.org/forum/mbed/topic/2774/?page=1#comment-14221 + template<typename T> void attach( T *optr, uint32_t ( T::*fptr )( uint32_t ) ){ + FPointer* hook = new FPointer(); + hook->attach(optr, fptr); + this->hooks.push_back(hook); + } + + template<typename T> void reset_attach( T *optr, uint32_t ( T::*fptr )( uint32_t ) ){ + FPointer* reset_hook = new FPointer(); + reset_hook->attach(optr, fptr); + this->reset_hooks.push_back(reset_hook); + } + + + vector<FPointer*> hooks; + vector<FPointer*> reset_hooks; + double frequency; + +}; + + + + + + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/StreamOutput.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,22 @@ +#ifndef STREAMOUTPUT_H +#define STREAMOUTPUT_H + +class StreamOutput { + public: + StreamOutput(){} + virtual int printf(const char* format, ...){return 0;} + + + + +}; + + + + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/nuts_bolts.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,43 @@ +/* +nuts_bolts.h - cartesian robot controller. +Part of Grbl + +Copyright (c) 2009-2011 Simen Svale Skogsrud + +Grbl is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Grbl is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Grbl. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef nuts_bolts_h +#define nuts_bolts_h + +#include <string> +using std::string; + +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 + +#define ALPHA_STEPPER 0 +#define BETA_STEPPER 1 +#define GAMMA_STEPPER 2 + +#define clear_vector(a) memset(a, 0, sizeof(a)) +#define clear_vector_double(a) memset(a, 0.0, sizeof(a)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +#define dd(...) LPC_GPIO2->FIODIR = 0xffff; LPC_GPIO2->FIOCLR = 0xffff; LPC_GPIO2->FIOSET = __VA_ARGS__ + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/utils.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,95 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Kernel.h" +#include "libs/utils.h" +#include "system_LPC17xx.h" +using namespace std; +#include <string> +using std::string; +#include <cstring> + + +uint16_t get_checksum(string to_check){ + // From:Â http://en.wikipedia.org/wiki/Fletcher%27s_checksum + uint16_t sum1 = 0; + uint16_t sum2 = 0; + for( int index = 0; index < to_check.length(); ++index ){ + sum1 = (sum1 + to_check[index]) % 255; + sum2 = (sum2 + sum1) % 255; + } + return (sum2 << 8) | sum1; +} + +vector<uint16_t> get_checksums(string key){ + key = key.append(" "); + vector<uint16_t> check_sums; + size_t begin_key = 0; + while( begin_key < key.size()-1 ){ + size_t end_key = key.find_first_of(" .", begin_key); + string key_node = key.substr(begin_key, end_key - begin_key); + check_sums.push_back(get_checksum(key_node)); + begin_key = end_key + 1; + } + return check_sums; +} + +// Convert to lowercase +string lc(string str){ + for (int i=0;i<strlen(str.c_str());i++) + if (str[i] >= 0x41 && str[i] <= 0x5A) + str[i] = str[i] + 0x20; + return str; +} + +// Remove non-number characters +string remove_non_number( string str ){ + string number_mask = "0123456789-."; + size_t found=str.find_first_not_of(number_mask); + while (found!=string::npos){ + //str[found]='*'; + str.replace(found,1,""); + found=str.find_first_not_of(number_mask); + } + return str; +} + +// Get the first parameter, and remove it from the original string +string shift_parameter( string ¶meters ){ + size_t beginning = parameters.find_first_of(" "); + if( beginning == string::npos ){ string temp = parameters; parameters = ""; return temp; } + string temp = parameters.substr( 0, beginning ); + parameters = parameters.substr(beginning+1, parameters.size()); + return temp; +} + +// Separate command from arguments +string get_arguments( string possible_command ){ + size_t beginning = possible_command.find_first_of(" "); + if( beginning == string::npos ){ return ""; } + return possible_command.substr( beginning+1, possible_command.size() - beginning); +} + +// Returns true if the file exists +bool file_exists( string file_name ){ + bool exists = false; + FILE *lp = fopen(file_name.c_str(), "r"); + if(lp){ exists = true; } + fclose(lp); + return exists; +} + +// Prepares and executes a watchdog reset +void system_reset( void ){ + LPC_WDT->WDCLKSEL = 0x1; // Set CLK src to PCLK + uint32_t clk = SystemCoreClock / 16; // WD has a fixed /4 prescaler, PCLK default is /4 + LPC_WDT->WDTC = 1 * (float)clk; // Reset in 1 second + LPC_WDT->WDMOD = 0x3; // Enabled and Reset + LPC_WDT->WDFEED = 0xAA; // Kick the dog! + LPC_WDT->WDFEED = 0x55; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/utils.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,29 @@ +#ifndef utils_h +#define utils_h + +#include <stdint.h> +using namespace std; +#include <string> +#include <vector> +using std::string; + +string lc(string str); + +string remove_non_number( string str ); + +uint16_t get_checksum(string to_check); + +vector<uint16_t> get_checksums(string key); + +string shift_parameter( string ¶meters ); + +string get_arguments( string possible_command ); + +bool file_exists( string file_name ); + +void system_reset( void ); + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,54 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Kernel.h" +#include "modules/tools/laser/Laser.h" +#include "modules/tools/spindle/Spindle.h" +#include "modules/tools/extruder/Extruder.h" +#include "modules/tools/temperaturecontrol/TemperatureControlPool.h" +#include "modules/robot/Player.h" +#include "modules/utils/simpleshell/SimpleShell.h" +#include "modules/utils/configurator/Configurator.h" +#include "modules/utils/currentcontrol/CurrentControl.h" +#include "modules/utils/pausebutton/PauseButton.h" +//#include "libs/ChaNFSSD/SDFileSystem.h" +#include "libs/Config.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" + +//#include "libs/USBCDCMSC/USBCDCMSC.h" +LocalFileSystem local("local"); +//SDFileSystem sd(p5, p6, p7, p8, "sd"); // LPC17xx specific : comment if you are not using a SD�card ( for example with a mBed ). +//LocalFileSystem local("local"); // LPC17xx specific :�comment if you are not running a mBed +//USBCDCMSC cdcmsc(&sd); // LPC17xx specific :�Composite serial + msc USB device + +int main() { + + Kernel* kernel = new Kernel(); + + kernel->serial->printf("Smoothie ( grbl port ) version 0.6.1_mbed \r\n"); + + + + //kernel->add_module( new Laser(p21) ); + //kernel->add_module( new Extruder() ); + kernel->add_module( new Spindle() ); + kernel->add_module( new SimpleShell() ); + kernel->add_module( new Configurator() ); + kernel->add_module( new CurrentControl() ); + kernel->add_module( new TemperatureControlPool() ); + kernel->add_module( new PauseButton() ); + + //kernel->add_module( &cdcmsc ); + + kernel->serial->printf("start\r\n"); + + while(1){ + kernel->call_event(ON_MAIN_LOOP); + kernel->call_event(ON_IDLE); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/10b9abbe79a6 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/communication/GcodeDispatch.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,115 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string> +using std::string; +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "utils/Gcode.h" +#include "libs/nuts_bolts.h" +#include "GcodeDispatch.h" +#include "modules/robot/Player.h" +#include "libs/SerialMessage.h" +#include "libs/StreamOutput.h" + +GcodeDispatch::GcodeDispatch(){} + +// Called when the module has just been loaded +void GcodeDispatch::on_module_loaded() { + this->register_for_event(ON_CONSOLE_LINE_RECEIVED); + currentline = -1; +} + +// When a command is received, if it is a Gcode, dispatch it as an object via an event +void GcodeDispatch::on_console_line_received(void * line){ + SerialMessage new_message = *static_cast<SerialMessage*>(line); + string possible_command = new_message.message; + + char first_char = possible_command[0]; + int ln = 0; + int cs = 0; + if( first_char == 'G' || first_char == 'M' || first_char == 'T' || first_char == 'N' ){ + + //Get linenumber + if( first_char == 'N' ){ + Gcode full_line = Gcode(); + full_line.command = possible_command; + full_line.stream = new_message.stream; + ln = (int) full_line.get_value('N'); + int chksum = (int) full_line.get_value('*'); + + //Catch message if it is M110: Set Current Line Number + if( full_line.has_letter('M') ){ + if( ((int) full_line.get_value('M')) == 110 ){ + currentline = ln; + new_message.stream->printf("ok\r\n"); + return; + } + } + + //Strip checksum value from possible_command + size_t chkpos = possible_command.find_first_of("*"); + possible_command = possible_command.substr(0, chkpos); + //Calculate checksum + if( chkpos != string::npos ){ + for(int i = 0; possible_command[i] != '*' && possible_command[i] != NULL; i++) + cs = cs ^ possible_command[i]; + cs &= 0xff; // Defensive programming... + cs -= chksum; + } + //Strip line number value from possible_command + size_t lnsize = possible_command.find_first_of(" ") + 1; + possible_command = possible_command.substr(lnsize); + + }else{ + //Assume checks succeeded + cs = 0x00; + ln = currentline + 1; + } + + //Remove comments + size_t comment = possible_command.find_first_of(";"); + if( comment != string::npos ){ possible_command = possible_command.substr(0, comment); } + + //If checksum passes then process message, else request resend + int nextline = currentline + 1; + if( cs == 0x00 && ln == nextline ){ + if( first_char == 'N' ) { + currentline = nextline; + } + + while(possible_command.size() > 0) { + size_t nextcmd = possible_command.find_first_of("GMT", possible_command.find_first_of("GMT")+1); + string single_command; + if(nextcmd == string::npos) { + single_command = possible_command; + possible_command = ""; + } + else { + single_command = possible_command.substr(0,nextcmd); + possible_command = possible_command.substr(nextcmd); + } + //Prepare gcode for dispatch + Gcode gcode = Gcode(); + gcode.command = single_command; + gcode.stream = new_message.stream; + + //Dispatch message! + this->kernel->call_event(ON_GCODE_RECEIVED, &gcode ); + new_message.stream->printf("ok\r\n"); + } + }else{ + //Request resend + new_message.stream->printf("rs N%d\r\n", nextline); + } + + // Ignore comments and blank lines + }else if( first_char == ';' || first_char == '(' || first_char == ' ' || first_char == '\n' || first_char == '\r' ){ + new_message.stream->printf("ok\r\n"); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/communication/GcodeDispatch.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,29 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef GCODE_DISPATCH_H +#define GCODE_DISPATCH_H + +#include <string> +using std::string; +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "utils/Gcode.h" + +#include "libs/StreamOutput.h" + +class GcodeDispatch : public Module { + public: + GcodeDispatch(); + + virtual void on_module_loaded(); + virtual void on_console_line_received(void* line); + private: + int currentline; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/communication/SerialConsole.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,88 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string> +#include <stdarg.h> +using std::string; +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "SerialConsole.h" +#include "libs/RingBuffer.h" +#include "libs/SerialMessage.h" +#include "libs/StreamOutput.h" + +// Serial reading module +// Treats every received line as a command and passes it ( via event call ) to the command dispatcher. +// The command dispatcher will then ask other modules if they can do something with it +SerialConsole::SerialConsole( PinName rx_pin, PinName tx_pin, int baud_rate ){ + this->serial = new mbed::Serial( rx_pin, tx_pin ); + this->serial->printf("T1\n"); + this->serial->baud(baud_rate); +} + +// Called when the module has just been loaded +void SerialConsole::on_module_loaded() { + // We want to be called every time a new char is received + this->serial->attach(this, &SerialConsole::on_serial_char_received, mbed::Serial::RxIrq); + + // We only call the command dispatcher in the main loop, nowhere else + this->register_for_event(ON_MAIN_LOOP); +} + +// Called on Serial::RxIrq interrupt, meaning we have received a char +void SerialConsole::on_serial_char_received(){ + while(this->serial->readable()){ + char received = this->serial->getc(); + // convert CR to NL (for host OSs that don't send NL) + if( received == '\r' ){ received = '\n'; } + this->buffer.push_back(received); + } +} + +// Actual event calling must happen in the main loop because if it happens in the interrupt we will loose data +void SerialConsole::on_main_loop(void * argument){ + if( this->has_char('\n') ){ + int index = 0; + string received; + while(1){ + char c; + this->buffer.pop_front(c); + if( c == '\n' ){ + struct SerialMessage message; + message.message = received; + message.stream = this; + this->kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message ); + return; + }else{ + received += c; + } + } + } +} + + +int SerialConsole::printf(const char* format, ...){ + va_list args; + int result=0; + va_start (args, format); + this->serial->printf( format, args); + va_end (args); + return result; +} + + +bool SerialConsole::has_char(char letter){ + int index = this->buffer.head; + while( index != this->buffer.tail ){ + if( this->buffer.buffer[index] == letter ){ + return true; + } + index = this->buffer.next_block_index(index); + } + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/communication/SerialConsole.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,39 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef SERIALCONSOLE_H +#define SERIALCONSOLE_H + +#include "libs/Module.h" +#include "mbed.h" +#include "libs/Kernel.h" +#include <vector> +#include <string> +using std::string; +#include "libs/RingBuffer.h" +#include "libs/StreamOutput.h" + + +#define baud_rate_setting_checksum 10922 + +class SerialConsole : public Module, public StreamOutput { + public: + SerialConsole( PinName rx_pin, PinName tx_pin, int baud_rate ); + + virtual void on_module_loaded(); + void on_serial_char_received(); + virtual void on_main_loop(void * argument); + bool has_char(char letter); + virtual int printf(const char* format, ...); + + //string receive_buffer; // Received chars are stored here until a newline character is received + //vector<std::string> received_lines; // Received lines are stored here until they are requested + RingBuffer<char,256> buffer; // Receive buffer + mbed::Serial* serial; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/communication/utils/Gcode.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,53 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include <string> +using std::string; +#include "Gcode.h" +#include "libs/StreamOutput.h" + +#include <stdlib.h> + + +Gcode::Gcode(){} + +// Whether or not a Gcode has a letter +bool Gcode::has_letter( char letter ){ + //return ( this->command->find( letter ) != string::npos ); + for (size_t i=0; i < this->command.length(); i++){ + if( this->command.at(i) == letter ){ + return true; + } + } + return false; +} + +// Retrieve the value for a given letter +// We don't use the high-level methods of std::string because they call malloc and it's very bad to do that inside of interrupts +double Gcode::get_value( char letter ){ + //__disable_irq(); + for (size_t i=0; i <= this->command.length()-1; i++){ + if( letter == this->command.at(i) ){ + size_t beginning = i+1; + char buffer[20]; + for(size_t j=beginning; j <= this->command.length(); j++){ + char c; + if( j == this->command.length() ){ c = ';'; }else{ c = this->command.at(j); } + if( c != '.' && c != '-' && ( c < '0' || c > '9' ) ){ + buffer[j-beginning] = '\0'; + //__enable_irq(); + return atof(buffer); + }else{ + buffer[j-beginning] = c; + } + } + } + } + //__enable_irq(); + return 0; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/communication/utils/Gcode.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,30 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#ifndef GCODE_H +#define GCODE_H +#include <string> +using std::string; +#include "libs/StreamOutput.h" +// Object to represent a Gcode comman +#include <stdlib.h> + +class Gcode { + public: + Gcode(); + bool has_letter( char letter ); + double get_value ( char letter ); + + string command; + double millimeters_of_travel; + bool call_on_gcode_execute_event_immediatly; + bool on_gcode_execute_event_called; + + StreamOutput* stream; +}; +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Block.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,221 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include <math.h> +#include <string> +#include "Block.h" +#include "Planner.h" +#include "Player.h" +using std::string; +#include <vector> +#include "../communication/utils/Gcode.h" + +Block::Block(){ + clear_vector(this->steps); + this->times_taken = 0; // A block can be "taken" by any number of modules, and the next block is not moved to until all the modules have "released" it. This value serves as a tracker. + this->is_ready = false; + this->initial_rate = -1; + this->final_rate = -1; +} + +void Block::debug(Kernel* kernel){ + kernel->serial->printf("%p: steps:%4d|%4d|%4d(max:%4d) nominal:r%10d/s%6.1f mm:%9.6f rdelta:%8d acc:%5d dec:%5d rates:%10d>%10d taken:%d ready:%d \r\n", this, this->steps[0], this->steps[1], this->steps[2], this->steps_event_count, this->nominal_rate, this->nominal_speed, this->millimeters, this->rate_delta, this->accelerate_until, this->decelerate_after, this->initial_rate, this->final_rate, this->times_taken, this->is_ready ); +} + + +// Calculate a braking factor to reach baseline speed which is max_jerk/2, e.g. the +// speed under which you cannot exceed max_jerk no matter what you do. +double Block::compute_factor_for_safe_speed(){ + return( this->planner->max_jerk / this->nominal_speed ); +} + + +// Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. +// The factors represent a factor of braking and must be in the range 0.0-1.0. +// +--------+ <- nominal_rate +// / \ +// nominal_rate*entry_factor -> + \ +// | + <- nominal_rate*exit_factor +// +-------------+ +// time --> +void Block::calculate_trapezoid( double entryfactor, double exitfactor ){ + + this->initial_rate = ceil(this->nominal_rate * entryfactor); // (step/min) + this->final_rate = ceil(this->nominal_rate * exitfactor); // (step/min) + double acceleration_per_minute = this->rate_delta * this->planner->kernel->stepper->acceleration_ticks_per_second * 60.0; + int accelerate_steps = ceil( this->estimate_acceleration_distance( this->initial_rate, this->nominal_rate, acceleration_per_minute ) ); + int decelerate_steps = ceil( this->estimate_acceleration_distance( this->nominal_rate, this->final_rate, -acceleration_per_minute ) ); + + // Calculate the size of Plateau of Nominal Rate. + int plateau_steps = this->steps_event_count-accelerate_steps-decelerate_steps; + + // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will + // have to use intersection_distance() to calculate when to abort acceleration and start braking + // in order to reach the final_rate exactly at the end of this block. + if (plateau_steps < 0) { + accelerate_steps = ceil(this->intersection_distance(this->initial_rate, this->final_rate, acceleration_per_minute, this->steps_event_count)); + accelerate_steps = max( accelerate_steps, 0 ); // Check limits due to numerical round-off + accelerate_steps = min( accelerate_steps, int(this->steps_event_count) ); + plateau_steps = 0; + } + + this->accelerate_until = accelerate_steps; + this->decelerate_after = accelerate_steps+plateau_steps; + + // TODO: FIX THIS: DIRTY HACK so that we don't end too early for blocks with 0 as final_rate. Doing the math right would be better. Probably fixed in latest grbl + if( this->final_rate < 0.01 ){ + this->decelerate_after += ( this->nominal_rate / 60 / this->planner->kernel->stepper->acceleration_ticks_per_second ) * 3; + } + +} + +// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the +// given acceleration: +double Block::estimate_acceleration_distance(double initialrate, double targetrate, double acceleration) { + return( (targetrate*targetrate-initialrate*initialrate)/(2L*acceleration)); +} + +// This function gives you the point at which you must start braking (at the rate of -acceleration) if +// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after +// a total travel of distance. This can be used to compute the intersection point between acceleration and +// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) +// +/* + <- some maximum rate we don't care about + /|\ + / | \ + / | + <- final_rate + / | | + initial_rate -> +----+--+ + ^ ^ + | | + intersection_distance distance */ +double Block::intersection_distance(double initialrate, double finalrate, double acceleration, double distance) { + return((2*acceleration*distance-initialrate*initialrate+finalrate*finalrate)/(4*acceleration)); +} + +// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the +// acceleration within the allotted distance. +inline double max_allowable_speed(double acceleration, double target_velocity, double distance) { + return( + sqrt(target_velocity*target_velocity-2L*acceleration*60*60*distance) //Was acceleration*60*60*distance, in case this breaks, but here we prefer to use seconds instead of minutes + ); +} + + +// Called by Planner::recalculate() when scanning the plan from last to first entry. +void Block::reverse_pass(Block* next, Block* previous){ + + if (next) { + // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. + // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and + // check for maximum allowable speed reductions to ensure maximum possible planned speed. + if (this->entry_speed != this->max_entry_speed) { + + // If nominal length true, max junction speed is guaranteed to be reached. Only compute + // for max allowable speed if block is decelerating and nominal length is false. + if ((!this->nominal_length_flag) && (this->max_entry_speed > next->entry_speed)) { + this->entry_speed = min( this->max_entry_speed, max_allowable_speed(-this->planner->acceleration,next->entry_speed,this->millimeters)); + } else { + this->entry_speed = this->max_entry_speed; + } + this->recalculate_flag = true; + + } + } // Skip last block. Already initialized and set for recalculation. + +} + + +// Called by Planner::recalculate() when scanning the plan from first to last entry. +void Block::forward_pass(Block* previous, Block* next){ + + if(!previous) { return; } // Begin planning after buffer_tail + + // If the previous block is an acceleration block, but it is not long enough to complete the + // full speed change within the block, we need to adjust the entry speed accordingly. Entry + // speeds have already been reset, maximized, and reverse planned by reverse planner. + // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. + if (!previous->nominal_length_flag) { + if (previous->entry_speed < this->entry_speed) { + double entry_speed = min( this->entry_speed, + max_allowable_speed(-this->planner->acceleration,previous->entry_speed,previous->millimeters) ); + + // Check for junction speed change + if (this->entry_speed != entry_speed) { + this->entry_speed = entry_speed; + this->recalculate_flag = true; + } + } + } + +} + + +// Gcodes are attached to their respective blocks so that on_gcode_execute can be called with it +void Block::append_gcode(Gcode* gcode){ + __disable_irq(); + this->gcodes.push_back(*gcode); + __enable_irq(); +} + +// The attached gcodes are then poped and the on_gcode_execute event is called with them as a parameter +void Block::pop_and_execute_gcode(Kernel* &kernel){ + Block* block = const_cast<Block*>(this); + for(unsigned short index=0; index<block->gcodes.size(); index++){ + kernel->call_event(ON_GCODE_EXECUTE, &(block->gcodes[index])); + } +} + +// Signal the player that this block is ready to be injected into the system +void Block::ready(){ + this->is_ready = true; + this->player->new_block_added(); +} + +// Mark the block as taken by one more module +void Block::take(){ + this->times_taken++; +} + +// Mark the block as no longer taken by one module, go to next block if this free's it +void Block::release(){ + this->times_taken--; + if( this->times_taken < 1 ){ + this->player->kernel->call_event(ON_BLOCK_END, this); + this->pop_and_execute_gcode(this->player->kernel); + Player* player = this->player; + + if( player->queue.size() > 0 ){ + player->queue.delete_first(); + } + + if( player->looking_for_new_block == false ){ + if( player->queue.size() > 0 ){ + Block* candidate = player->queue.get_ref(0); + if( candidate->is_ready ){ + player->current_block = candidate; + player->kernel->call_event(ON_BLOCK_BEGIN, player->current_block); + if( player->current_block->times_taken < 1 ){ + player->current_block->release(); + } + }else{ + + player->current_block = NULL; + + } + }else{ + player->current_block = NULL; + } + } + } +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Block.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,81 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef BLOCK_H +#define BLOCK_H +#include "libs/Module.h" +#include "libs/Kernel.h" +using namespace std; +#include <string> +#include <vector> + +#include "../communication/utils/Gcode.h" +#include "Planner.h" +class Planner; +class Player; + +double max_allowable_speed( double acceleration, double target_velocity, double distance); + +class Block { + public: + Block(); + double compute_factor_for_safe_speed(); + void calculate_trapezoid( double entry_factor, double exit_factor ); + double estimate_acceleration_distance( double initial_rate, double target_rate, double acceleration ); + double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance); + void reverse_pass(Block* previous, Block* next); + void forward_pass(Block* previous, Block* next); + void debug(Kernel* kernel); + void append_gcode(Gcode* gcode); + void pop_and_execute_gcode(Kernel* &kernel); + double get_duration_left(unsigned int already_taken_steps); + void take(); + void release(); + void ready(); + + vector<std::string> commands; + vector<double> travel_distances; + vector<Gcode> gcodes; + + unsigned int steps[3]; // Number of steps for each axis for this block + unsigned int steps_event_count; // Steps for the longest axis + unsigned int nominal_rate; // Nominal rate in steps per minute + float nominal_speed; // Nominal speed in mm per minute + float millimeters; // Distance for this move + double entry_speed; + unsigned int rate_delta; // Nomber of steps to add to the speed for each acceleration tick + int initial_rate; // Initial speed in steps per minute + int final_rate; // Final speed in steps per minute + unsigned int accelerate_until; // Stop accelerating after this number of steps + unsigned int decelerate_after; // Start decelerating after this number of steps + unsigned int direction_bits; // Direction for each axis in bit form, relative to the direction port's mask + + + uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction + uint8_t nominal_length_flag; // Planner flag for nominal speed always reached + + double max_entry_speed; + Planner* planner; + Player* player; + + bool is_ready; + + short times_taken; // A block can be "taken" by any number of modules, and the next block is not moved to until all the modules have "released" it. This value serves as a tracker. + +}; + + + + + + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Planner.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,264 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl) + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/RingBuffer.h" +#include "../communication/utils/Gcode.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "Block.h" +#include "Planner.h" +#include "Player.h" + + +Planner::Planner(){ + clear_vector(this->position); + clear_vector_double(this->previous_unit_vec); + this->previous_nominal_speed = 0.0; + this->has_deleted_block = false; +} + +void Planner::on_module_loaded(){ + this->on_config_reload(this); +} + +void Planner::on_config_reload(void* argument){ + this->acceleration = this->kernel->config->value(acceleration_checksum )->by_default(100 )->as_number(); + this->max_jerk = this->kernel->config->value(max_jerk_checksum )->by_default(100 )->as_number(); + this->junction_deviation = this->kernel->config->value(junction_deviation_checksum )->by_default(0.05)->as_number(); +} + + +// Append a block to the queue, compute it's speed factors +void Planner::append_block( int target[], double feed_rate, double distance, double deltas[] ){ + + // Stall here if the queue is ful + this->kernel->player->wait_for_queue(2); + + Block* block = this->kernel->player->new_block(); + block->planner = this; + + // Direction bits + block->direction_bits = 0; + for( int stepper=ALPHA_STEPPER; stepper<=GAMMA_STEPPER; stepper++){ + if( target[stepper] < position[stepper] ){ block->direction_bits |= (1<<stepper); } + } + + // Number of steps for each stepper + for( int stepper=ALPHA_STEPPER; stepper<=GAMMA_STEPPER; stepper++){ block->steps[stepper] = labs(target[stepper] - this->position[stepper]); } + + // Max number of steps, for all axes + block->steps_event_count = max( block->steps[ALPHA_STEPPER], max( block->steps[BETA_STEPPER], block->steps[GAMMA_STEPPER] ) ); + //if( block->steps_event_count == 0 ){ this->computing = false; return; } + + block->millimeters = distance; + double inverse_millimeters = 0; + if( distance > 0 ){ inverse_millimeters = 1.0/distance; } + + // Calculate speed in mm/minute for each axis. No divide by zero due to previous checks. + // NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c + double inverse_minute = feed_rate * inverse_millimeters; + if( distance > 0 ){ + block->nominal_speed = block->millimeters * inverse_minute; // (mm/min) Always > 0 + block->nominal_rate = ceil(block->steps_event_count * inverse_minute); // (step/min) Always > 0 + }else{ + block->nominal_speed = 0; + block->nominal_rate = 0; + } + + //this->kernel->serial->printf("nom_speed: %f nom_rate: %u step_event_count: %u block->steps_z: %u \r\n", block->nominal_speed, block->nominal_rate, block->steps_event_count, block->steps[2] ); + + // Compute the acceleration rate for the trapezoid generator. Depending on the slope of the line + // average travel per step event changes. For a line along one axis the travel per step event + // is equal to the travel/step in the particular axis. For a 45 degree line the steppers of both + // axes might step for every step event. Travel per step event is then sqrt(travel_x^2+travel_y^2). + // To generate trapezoids with contant acceleration between blocks the rate_delta must be computed + // specifically for each line to compensate for this phenomenon: + // Convert universal acceleration for direction-dependent stepper rate change parameter + block->rate_delta = ceil( block->steps_event_count*inverse_millimeters * this->acceleration*60.0 / this->kernel->stepper->acceleration_ticks_per_second ); // (step/min/acceleration_tick) + + // Compute path unit vector + double unit_vec[3]; + unit_vec[X_AXIS] = deltas[X_AXIS]*inverse_millimeters; + unit_vec[Y_AXIS] = deltas[Y_AXIS]*inverse_millimeters; + unit_vec[Z_AXIS] = deltas[Z_AXIS]*inverse_millimeters; + + // Compute maximum allowable entry speed at junction by centripetal acceleration approximation. + // Let a circle be tangent to both previous and current path line segments, where the junction + // deviation is defined as the distance from the junction to the closest edge of the circle, + // colinear with the circle center. The circular segment joining the two paths represents the + // path of centripetal acceleration. Solve for max velocity based on max acceleration about the + // radius of the circle, defined indirectly by junction deviation. This may be also viewed as + // path width or max_jerk in the previous grbl version. This approach does not actually deviate + // from path, but used as a robust way to compute cornering speeds, as it takes into account the + // nonlinearities of both the junction angle and junction velocity. + double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed + + if (this->kernel->player->queue.size() > 1 && (this->previous_nominal_speed > 0.0)) { + // Compute cosine of angle between previous and current path. (prev_unit_vec is negative) + // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity. + double cos_theta = - this->previous_unit_vec[X_AXIS] * unit_vec[X_AXIS] + - this->previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS] + - this->previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ; + + // Skip and use default max junction speed for 0 degree acute junction. + if (cos_theta < 0.95) { + vmax_junction = min(this->previous_nominal_speed,block->nominal_speed); + // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds. + if (cos_theta > -0.95) { + // Compute maximum junction velocity based on maximum acceleration and junction deviation + double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive. + vmax_junction = min(vmax_junction, + sqrt(this->acceleration*60*60 * this->junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) ); + } + } + } + block->max_entry_speed = vmax_junction; + + // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. + double v_allowable = this->max_allowable_speed(-this->acceleration,0.0,block->millimeters); //TODO:Â Get from config + block->entry_speed = min(vmax_junction, v_allowable); + + // Initialize planner efficiency flags + // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. + // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then + // the current block and next block junction speeds are guaranteed to always be at their maximum + // junction speeds in deceleration and acceleration, respectively. This is due to how the current + // block nominal speed limits both the current and next maximum junction speeds. Hence, in both + // the reverse and forward planners, the corresponding block junction speed will always be at the + // the maximum junction speed and may always be ignored for any speed reduction checks. + if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; } + else { block->nominal_length_flag = false; } + block->recalculate_flag = true; // Always calculate trapezoid for new block + + // Update previous path unit_vector and nominal speed + memcpy(this->previous_unit_vec, unit_vec, sizeof(unit_vec)); // previous_unit_vec[] = unit_vec[] + this->previous_nominal_speed = block->nominal_speed; + + // Update current position + memcpy(this->position, target, sizeof(int)*3); + + // Math-heavy re-computing of the whole queue to take the new + this->recalculate(); + + // The block can now be used + block->ready(); + +} + + +// Recalculates the motion plan according to the following algorithm: +// +// 1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_factor) +// so that: +// a. The junction jerk is within the set limit +// b. No speed reduction within one block requires faster deceleration than the one, true constant +// acceleration. +// 2. Go over every block in chronological order and dial down junction speed reduction values if +// a. The speed increase within one block would require faster accelleration than the one, true +// constant acceleration. +// +// When these stages are complete all blocks have an entry_factor that will allow all speed changes to +// be performed using only the one, true constant acceleration, and where no junction jerk is jerkier than +// the set limit. Finally it will: +// +// 3. Recalculate trapezoids for all blocks. +// +void Planner::recalculate() { + //this->kernel->serial->printf("recalculate last: %p, queue size: %d \r\n", this->kernel->player->queue.get_ref( this->kernel->player->queue.size()-1 ), this->kernel->player->queue.size() ); + this->reverse_pass(); + this->forward_pass(); + this->recalculate_trapezoids(); +} + +// Planner::recalculate() needs to go over the current plan twice. Once in reverse and once forward. This +// implements the reverse pass. +void Planner::reverse_pass(){ + // For each block + int block_index = this->kernel->player->queue.tail; + Block* blocks[3] = {NULL,NULL,NULL}; + + while(block_index!=this->kernel->player->queue.head){ + block_index = this->kernel->player->queue.prev_block_index( block_index ); + blocks[2] = blocks[1]; + blocks[1] = blocks[0]; + blocks[0] = &this->kernel->player->queue.buffer[block_index]; + if( blocks[1] == NULL ){ continue; } + blocks[1]->reverse_pass(blocks[2], blocks[0]); + } + +} + +// Planner::recalculate() needs to go over the current plan twice. Once in reverse and once forward. This +// implements the forward pass. +void Planner::forward_pass() { + // For each block + int block_index = this->kernel->player->queue.head; + Block* blocks[3] = {NULL,NULL,NULL}; + + while(block_index!=this->kernel->player->queue.tail){ + blocks[0] = blocks[1]; + blocks[1] = blocks[2]; + blocks[2] = &this->kernel->player->queue.buffer[block_index]; + if( blocks[0] == NULL ){ continue; } + blocks[1]->forward_pass(blocks[0],blocks[2]); + block_index = this->kernel->player->queue.next_block_index( block_index ); + } + blocks[2]->forward_pass(blocks[1],NULL); + +} + +// Recalculates the trapezoid speed profiles for flagged blocks in the plan according to the +// entry_speed for each junction and the entry_speed of the next junction. Must be called by +// planner_recalculate() after updating the blocks. Any recalulate flagged junction will +// compute the two adjacent trapezoids to the junction, since the junction speed corresponds +// to exit speed and entry speed of one another. +void Planner::recalculate_trapezoids() { + int block_index = this->kernel->player->queue.head; + Block* current; + Block* next = NULL; + + while(block_index != this->kernel->player->queue.tail){ + current = next; + next = &this->kernel->player->queue.buffer[block_index]; + //this->kernel->serial->printf("index:%d current:%p next:%p \r\n", block_index, current, next ); + if( current ){ + // Recalculate if current block entry or exit junction speed has changed. + if( current->recalculate_flag || next->recalculate_flag ){ + current->calculate_trapezoid( current->entry_speed/current->nominal_speed, next->entry_speed/current->nominal_speed ); + current->recalculate_flag = false; + } + } + block_index = this->kernel->player->queue.next_block_index( block_index ); + } + + // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. + next->calculate_trapezoid( next->entry_speed/next->nominal_speed, MINIMUM_PLANNER_SPEED/next->nominal_speed); //TODO: Make configuration option + next->recalculate_flag = false; + +} + +// Debug function +void Planner::dump_queue(){ + for( int index = 0; index <= this->kernel->player->queue.size()-1; index++ ){ + if( index > 10 && index < this->kernel->player->queue.size()-10 ){ continue; } + this->kernel->serial->printf("block %03d > ", index); + this->kernel->player->queue.get_ref(index)->debug(this->kernel); + } +} + +// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the +// acceleration within the allotted distance. +double Planner::max_allowable_speed(double acceleration, double target_velocity, double distance) { + return( + sqrt(target_velocity*target_velocity-2L*acceleration*60*60*distance) //Was acceleration*60*60*distance, in case this breaks, but here we prefer to use seconds instead of minutes + ); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Planner.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,54 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PLANNER_H +#define PLANNER_H + +#include <vector> +#include "libs/RingBuffer.h" +#include "../communication/utils/Gcode.h" +#include "Block.h" + +#define acceleration_checksum 25326 +#define max_jerk_checksum 61012 +#define junction_deviation_checksum 6035 + +// TODO: Get from config +#define MINIMUM_PLANNER_SPEED 0.0 +using namespace std; + + +class Planner : public Module { + public: + Planner(); + void append_block( int target[], double feed_rate, double distance, double deltas[] ); + double max_allowable_speed( double acceleration, double target_velocity, double distance); + void recalculate(); + void reverse_pass(); + void forward_pass(); + void recalculate_trapezoids(); + void dump_queue(); + Block* get_current_block(); + void cleanup_queue(); + virtual void on_module_loaded(); + virtual void on_config_reload(void* argument); + + int position[3]; // Current position, in steps + double previous_unit_vec[3]; + Block last_deleted_block; // Item -1 in the queue, TODO: Grbl does not need this, but Smoothie won't work without it, we are probably doing something wrong + bool has_deleted_block; // Flag for above value + float previous_nominal_speed; + + double acceleration; // Setting + double max_jerk; // Setting + double junction_deviation; // Setting + +}; + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Player.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,99 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl) + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +using namespace std; +#include <vector> +#include "libs/nuts_bolts.h" +#include "libs/RingBuffer.h" +#include "../communication/utils/Gcode.h" +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "Timer.h" // mbed.h lib +#include "wait_api.h" // mbed.h lib +#include "Block.h" +#include "Player.h" +#include "Planner.h" + +Player::Player(){ + this->current_block = NULL; + this->looking_for_new_block = false; +} + +// Append a block to the list +Block* Player::new_block(){ + + // Clean up the vector of commands in the block we are about to replace + // It is quite strange to do this here, we really should do it inside Block->pop_and_execute_gcode + // but that function is called inside an interrupt and thus can break everything if the interrupt was trigerred during a memory access + //Block* block = this->queue.get_ref( this->queue.size()-1 ); + Block* block = this->queue.get_ref( this->queue.size() ); + if( block->player == this ){ + for(short index=0; index<block->gcodes.size(); index++){ + block->gcodes.pop_back(); + } + } + + // Create a new virgin Block in the queue + this->queue.push_back(Block()); + block = this->queue.get_ref( this->queue.size()-1 ); + block->is_ready = false; + block->initial_rate = -2; + block->final_rate = -2; + block->player = this; + + return block; +} + +// Used by blocks to signal when they are ready to be used by the system +void Player::new_block_added(){ + if( this->current_block == NULL ){ + this->pop_and_process_new_block(33); + } +} + +// Process a new block in the queue +void Player::pop_and_process_new_block(int debug){ + if( this->looking_for_new_block ){ return; } + this->looking_for_new_block = true; + + if( this->current_block != NULL ){ this->looking_for_new_block = false; return; } + + // Return if queue is empty + if( this->queue.size() == 0 ){ + this->current_block = NULL; + // TODOÂ :Â ON_QUEUE_EMPTY event + this->looking_for_new_block = false; + return; + } + + // Get a new block + this->current_block = this->queue.get_ref(0); + + // Tell all modules about it + this->kernel->call_event(ON_BLOCK_BEGIN, this->current_block); + + // In case the module was not taken + if( this->current_block->times_taken < 1 ){ + this->looking_for_new_block = false; + this->current_block->release(); + } + + this->looking_for_new_block = false; + +} + +void Player::wait_for_queue(int free_blocks){ + mbed::Timer t; + while( this->queue.size() >= this->queue.capacity()-free_blocks ){ + t.reset(); + t.start(); + this->kernel->call_event(ON_IDLE); + t.stop(); + if(t.read_us() < 500) + wait_us(500 - t.read_us()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Player.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,33 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PLAYER_H +#define PLAYER_H +#include "libs/Module.h" +#include "libs/Kernel.h" +using namespace std; +#include <string> +#include <vector> + +class Player : public Module { + public: + Player(); + + Block* new_block(); + void new_block_added(); + void pop_and_process_new_block(int debug); + void wait_for_queue(int free_blocks); + + RingBuffer<Block,32> queue; // Queue of Blocks + Block* current_block; + bool looking_for_new_block; + +}; + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Robot.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,317 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl) + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include <string> +using std::string; +#include <math.h> +#include "Planner.h" +#include "Player.h" +#include "Robot.h" +#include "libs/nuts_bolts.h" +#include "../communication/utils/Gcode.h" +#include "arm_solutions/BaseSolution.h" +#include "arm_solutions/CartesianSolution.h" + +Robot::Robot(){ + this->inch_mode = false; + this->absolute_mode = true; + this->motion_mode = MOTION_MODE_SEEK; + this->select_plane(X_AXIS, Y_AXIS, Z_AXIS); + clear_vector(this->current_position); + clear_vector(this->last_milestone); +} + +//Called when the module has just been loaded +void Robot::on_module_loaded() { + this->arm_solution = new CartesianSolution(this->kernel->config); + this->register_for_event(ON_GCODE_RECEIVED); + + // Configuration + this->on_config_reload(this); +} + +void Robot::on_config_reload(void* argument){ + this->feed_rate = this->kernel->config->value(default_feed_rate_checksum )->by_default(100)->as_number()/60; + this->seek_rate = this->kernel->config->value(default_seek_rate_checksum )->by_default(100)->as_number()/60; + this->mm_per_line_segment = this->kernel->config->value(mm_per_line_segment_checksum)->by_default(0.1)->as_number(); + this->mm_per_arc_segment = this->kernel->config->value(mm_per_arc_segment_checksum )->by_default(10 )->as_number(); + this->arc_correction = this->kernel->config->value(arc_correction_checksum )->by_default(5 )->as_number(); + this->max_speeds[X_AXIS] = this->kernel->config->value(x_axis_max_speed_checksum )->by_default(0 )->as_number(); + this->max_speeds[Y_AXIS] = this->kernel->config->value(y_axis_max_speed_checksum )->by_default(0 )->as_number(); + this->max_speeds[Z_AXIS] = this->kernel->config->value(z_axis_max_speed_checksum )->by_default(0 )->as_number(); +} + +//A GCode has been received +void Robot::on_gcode_received(void * argument){ + Gcode* gcode = static_cast<Gcode*>(argument); + gcode->call_on_gcode_execute_event_immediatly = false; + gcode->on_gcode_execute_event_called = false; + //If the queue is empty, execute immediatly, otherwise attach to the last added block + if( this->kernel->player->queue.size() == 0 ){ + gcode->call_on_gcode_execute_event_immediatly = true; + this->execute_gcode(gcode); + if( gcode->on_gcode_execute_event_called == false ){ + this->kernel->call_event(ON_GCODE_EXECUTE, gcode ); + } + }else{ + Block* block = this->kernel->player->queue.get_ref( this->kernel->player->queue.size() - 1 ); + this->execute_gcode(gcode); + block->append_gcode(gcode); + } + +} + + +//See if the current Gcode line has some orders for us +void Robot::execute_gcode(Gcode* gcode){ + + //Temp variables, constant properties are stored in the object + uint8_t next_action = NEXT_ACTION_DEFAULT; + this->motion_mode = -1; + + //G-letter Gcodes are mostly what the Robot module is interrested in, other modules also catch the gcode event and do stuff accordingly + if( gcode->has_letter('G')){ + switch( (int) gcode->get_value('G') ){ + case 0: this->motion_mode = MOTION_MODE_SEEK; break; + case 1: this->motion_mode = MOTION_MODE_LINEAR; break; + case 2: this->motion_mode = MOTION_MODE_CW_ARC; break; + case 3: this->motion_mode = MOTION_MODE_CCW_ARC; break; + case 17: this->select_plane(X_AXIS, Y_AXIS, Z_AXIS); break; + case 18: this->select_plane(X_AXIS, Z_AXIS, Y_AXIS); break; + case 19: this->select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; + case 20:this->inch_mode = true; break; + case 21:this->inch_mode = false; break; + case 90:this->absolute_mode = true; break; + case 91:this->absolute_mode = false; break; + } + }else{ return; } + + //Get parameters + double target[3], offset[3]; + clear_vector(target); clear_vector(offset); + + memcpy(target, this->current_position, sizeof(target)); //default to last target + + for(char letter = 'I'; letter <= 'K'; letter++){ if( gcode->has_letter(letter) ){ offset[letter-'I'] = this->to_millimeters(gcode->get_value(letter)); } } + for(char letter = 'X'; letter <= 'Z'; letter++){ if( gcode->has_letter(letter) ){ target[letter-'X'] = this->to_millimeters(gcode->get_value(letter)) + ( this->absolute_mode ? 0 : target[letter-'X']); } } + + if( gcode->has_letter('F') ){ if( this->motion_mode == MOTION_MODE_SEEK ){ this->seek_rate = this->to_millimeters( gcode->get_value('F') ) / 60; }else{ this->feed_rate = this->to_millimeters( gcode->get_value('F') ) / 60; } } + + //Perform any physical actions + switch( next_action ){ + case NEXT_ACTION_DEFAULT: + switch(this->motion_mode){ + case MOTION_MODE_CANCEL: break; + case MOTION_MODE_SEEK : this->append_line(gcode, target, this->seek_rate ); break; + case MOTION_MODE_LINEAR: this->append_line(gcode, target, this->feed_rate ); break; + case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: this->compute_arc(gcode, offset, target ); break; + } + break; + } + + // As far as the parser is concerned, the position is now == target. In reality the + // motion control system might still be processing the action and the real tool position + // in any intermediate location. + memcpy(this->current_position, target, sizeof(double)*3); // this->position[] = target[]; + +} + +// Convert target from millimeters to steps, and append this to the planner +void Robot::append_milestone( double target[], double rate ){ + int steps[3]; //Holds the result of the conversion + + this->arm_solution->millimeters_to_steps( target, steps ); + + double deltas[3]; + for(int axis=X_AXIS;axis<=Z_AXIS;axis++){deltas[axis]=target[axis]-this->last_milestone[axis];} + + + double millimeters_of_travel = sqrt( pow( deltas[X_AXIS], 2 ) + pow( deltas[Y_AXIS], 2 ) + pow( deltas[Z_AXIS], 2 ) ); + + double duration = 0; + if( rate > 0 ){ duration = millimeters_of_travel / rate; } + + for(int axis=X_AXIS;axis<=Z_AXIS;axis++){ + if( this->max_speeds[axis] > 0 ){ + double axis_speed = ( fabs(deltas[axis]) / ( millimeters_of_travel / rate )) * 60; + if( axis_speed > this->max_speeds[axis] ){ + rate = rate * ( this->max_speeds[axis] / axis_speed ); + } + } + } + + this->kernel->planner->append_block( steps, rate*60, millimeters_of_travel, deltas ); + + memcpy(this->last_milestone, target, sizeof(double)*3); // this->last_milestone[] = target[]; + +} + +void Robot::append_line(Gcode* gcode, double target[], double rate ){ + + + // We cut the line into smaller segments. This is not usefull in a cartesian robot, but necessary for robots with rotational axes. + // In cartesian robot, a high "mm_per_line_segment" setting will prevent waste. + gcode->millimeters_of_travel = sqrt( pow( target[X_AXIS]-this->current_position[X_AXIS], 2 ) + pow( target[Y_AXIS]-this->current_position[Y_AXIS], 2 ) + pow( target[Z_AXIS]-this->current_position[Z_AXIS], 2 ) ); + + if( gcode->call_on_gcode_execute_event_immediatly == true ){ + this->kernel->call_event(ON_GCODE_EXECUTE, gcode ); + gcode->on_gcode_execute_event_called = true; + } + + if (gcode->millimeters_of_travel == 0.0) { + this->append_milestone(this->current_position, 0.0); + return; + } + + uint16_t segments = ceil( gcode->millimeters_of_travel/ this->mm_per_line_segment); + // A vector to keep track of the endpoint of each segment + double temp_target[3]; + //Initialize axes + memcpy( temp_target, this->current_position, sizeof(double)*3); // temp_target[] = this->current_position[]; + + //For each segment + for( int i=0; i<segments-1; i++ ){ + for(int axis=X_AXIS; axis <= Z_AXIS; axis++ ){ temp_target[axis] += ( target[axis]-this->current_position[axis] )/segments; } + this->append_milestone(temp_target, rate); + } + this->append_milestone(target, rate); +} + + +void Robot::append_arc(Gcode* gcode, double target[], double offset[], double radius, bool is_clockwise ){ + + double center_axis0 = this->current_position[this->plane_axis_0] + offset[this->plane_axis_0]; + double center_axis1 = this->current_position[this->plane_axis_1] + offset[this->plane_axis_1]; + double linear_travel = target[this->plane_axis_2] - this->current_position[this->plane_axis_2]; + double r_axis0 = -offset[this->plane_axis_0]; // Radius vector from center to current location + double r_axis1 = -offset[this->plane_axis_1]; + double rt_axis0 = target[this->plane_axis_0] - center_axis0; + double rt_axis1 = target[this->plane_axis_1] - center_axis1; + + // CCW angle between position and target from circle center. Only one atan2() trig computation required. + double angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); + if (angular_travel < 0) { angular_travel += 2*M_PI; } + if (is_clockwise) { angular_travel -= 2*M_PI; } + + gcode->millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel)); + + if( gcode->call_on_gcode_execute_event_immediatly == true ){ + this->kernel->call_event(ON_GCODE_EXECUTE, gcode ); + gcode->on_gcode_execute_event_called = true; + } + + if (gcode->millimeters_of_travel == 0.0) { + this->append_milestone(this->current_position, 0.0); + return; + } + + uint16_t segments = floor(gcode->millimeters_of_travel/this->mm_per_arc_segment); + + double theta_per_segment = angular_travel/segments; + double linear_per_segment = linear_travel/segments; + + /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, + and phi is the angle of rotation. Based on the solution approach by Jens Geisler. + r_T = [cos(phi) -sin(phi); + sin(phi) cos(phi] * r ; + For arc generation, the center of the circle is the axis of rotation and the radius vector is + defined from the circle center to the initial position. Each line segment is formed by successive + vector rotations. This requires only two cos() and sin() computations to form the rotation + matrix for the duration of the entire arc. Error may accumulate from numerical round-off, since + all double numbers are single precision on the Arduino. (True double precision will not have + round off issues for CNC applications.) Single precision error can accumulate to be greater than + tool precision in some cases. Therefore, arc path correction is implemented. + + Small angle approximation may be used to reduce computation overhead further. This approximation + holds for everything, but very small circles and large mm_per_arc_segment values. In other words, + theta_per_segment would need to be greater than 0.1 rad and N_ARC_CORRECTION would need to be large + to cause an appreciable drift error. N_ARC_CORRECTION~=25 is more than small enough to correct for + numerical drift error. N_ARC_CORRECTION may be on the order a hundred(s) before error becomes an + issue for CNC machines with the single precision Arduino calculations. + This approximation also allows mc_arc to immediately insert a line segment into the planner + without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied + a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead. + This is important when there are successive arc motions. + */ + // Vector rotation matrix values + double cos_T = 1-0.5*theta_per_segment*theta_per_segment; // Small angle approximation + double sin_T = theta_per_segment; + + double arc_target[3]; + double sin_Ti; + double cos_Ti; + double r_axisi; + uint16_t i; + int8_t count = 0; + + // Initialize the linear axis + arc_target[this->plane_axis_2] = this->current_position[this->plane_axis_2]; + + for (i = 1; i<segments; i++) { // Increment (segments-1) + + if (count < this->arc_correction ) { + // Apply vector rotation matrix + r_axisi = r_axis0*sin_T + r_axis1*cos_T; + r_axis0 = r_axis0*cos_T - r_axis1*sin_T; + r_axis1 = r_axisi; + count++; + } else { + // Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments. + // Compute exact location by applying transformation matrix from initial radius vector(=-offset). + cos_Ti = cos(i*theta_per_segment); + sin_Ti = sin(i*theta_per_segment); + r_axis0 = -offset[this->plane_axis_0]*cos_Ti + offset[this->plane_axis_1]*sin_Ti; + r_axis1 = -offset[this->plane_axis_0]*sin_Ti - offset[this->plane_axis_1]*cos_Ti; + count = 0; + } + + // Update arc_target location + arc_target[this->plane_axis_0] = center_axis0 + r_axis0; + arc_target[this->plane_axis_1] = center_axis1 + r_axis1; + arc_target[this->plane_axis_2] += linear_per_segment; + this->append_milestone(arc_target, this->feed_rate); + + } + // Ensure last segment arrives at target location. + this->append_milestone(target, this->feed_rate); +} + + +void Robot::compute_arc(Gcode* gcode, double offset[], double target[]){ + + // Find the radius + double radius = hypot(offset[this->plane_axis_0], offset[this->plane_axis_1]); + + // Set clockwise/counter-clockwise sign for mc_arc computations + bool is_clockwise = false; + if( this->motion_mode == MOTION_MODE_CW_ARC ){ is_clockwise = true; } + + // Append arc + this->append_arc(gcode, target, offset, radius, is_clockwise ); + +} + + +// Convert from inches to millimeters ( our internal storage unit ) if needed +inline double Robot::to_millimeters( double value ){ + return this->inch_mode ? value/25.4 : value; +} + +double Robot::theta(double x, double y){ + double t = atan(x/fabs(y)); + if (y>0) {return(t);} else {if (t>0){return(M_PI-t);} else {return(-M_PI-t);}} +} + +void Robot::select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2){ + this->plane_axis_0 = axis_0; + this->plane_axis_1 = axis_1; + this->plane_axis_2 = axis_2; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Robot.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,92 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef ROBOT_H +#define ROBOT_H + +#include <string> +using std::string; +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "../communication/utils/Gcode.h" +#include "arm_solutions/BaseSolution.h" +#include "Planner.h" + +#define default_seek_rate_checksum 6633 +#define default_feed_rate_checksum 47357 +#define mm_per_line_segment_checksum 30176 +#define mm_per_arc_segment_checksum 15470 +#define arc_correction_checksum 5074 +#define x_axis_max_speed_checksum 64935 +#define y_axis_max_speed_checksum 3752 +#define z_axis_max_speed_checksum 7849 + +#define NEXT_ACTION_DEFAULT 0 +#define NEXT_ACTION_DWELL 1 +#define NEXT_ACTION_GO_HOME 2 + +#define MOTION_MODE_SEEK 0 // G0 +#define MOTION_MODE_LINEAR 1 // G1 +#define MOTION_MODE_CW_ARC 2 // G2 +#define MOTION_MODE_CCW_ARC 3 // G3 +#define MOTION_MODE_CANCEL 4 // G80 + +#define PATH_CONTROL_MODE_EXACT_PATH 0 +#define PATH_CONTROL_MODE_EXACT_STOP 1 +#define PATH_CONTROL_MODE_CONTINOUS 2 + +#define PROGRAM_FLOW_RUNNING 0 +#define PROGRAM_FLOW_PAUSED 1 +#define PROGRAM_FLOW_COMPLETED 2 + +#define SPINDLE_DIRECTION_CW 0 +#define SPINDLE_DIRECTION_CCW 1 + +#define M_PI 3.14159 + + +class Robot : public Module { + public: + Robot(); + virtual void on_module_loaded(); + virtual void on_config_reload(void* argument); + virtual void on_gcode_received(void* argument); + void execute_gcode(Gcode* gcode); + void append_milestone( double target[], double feed_rate); + void append_line( Gcode* gcode, double target[], double feed_rate); + //void append_arc(double theta_start, double angular_travel, double radius, double depth, double rate); + void append_arc( Gcode* gcode, double target[], double offset[], double radius, bool is_clockwise ); + + + void compute_arc(Gcode* gcode, double offset[], double target[]); + double to_millimeters(double value); + double theta(double x, double y); + void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2); + + double current_position[3]; // Current position, in millimeters + double last_milestone[3]; // Last position, in millimeters + bool inch_mode; // true for inch mode, false for millimeter mode ( default ) + bool absolute_mode; // true for absolute mode ( default ), false for relative mode + int8_t motion_mode; // Motion mode for the current received Gcode + double seek_rate; // Current rate for seeking moves ( mm/s ) + double feed_rate; // Current rate for feeding moves ( mm/s ) + uint8_t plane_axis_0, plane_axis_1, plane_axis_2; // Current plane ( XY, XZ, YZ ) + BaseSolution* arm_solution; // Selected Arm solution ( millimeters to step calculation ) + double mm_per_line_segment; // Setting : Used to split lines into segments + double mm_per_arc_segment; // Setting : Used to split arcs into segmentrs + + // Number of arc generation iterations by small angle approximation before exact arc trajectory + // correction. This parameter maybe decreased if there are issues with the accuracy of the arc + // generations. In general, the default value is more than enough for the intended CNC applications + // of grbl, and should be on the order or greater than the size of the buffer to help with the + // computational efficiency of generating arcs. + int arc_correction; // Setting : how often to rectify arc computation + double max_speeds[3]; // Setting : max allowable speed in mm/m for each axis + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Stepper.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,243 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl) + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "Stepper.h" +#include "Planner.h" +#include "Player.h" +#include <vector> +using namespace std; +#include "libs/nuts_bolts.h" + +Stepper* stepper; + +Stepper::Stepper(){ + this->current_block = NULL; + this->step_events_completed = 0; + this->divider = 0; + this->paused = false; +} + +//Called when the module has just been loaded +void Stepper::on_module_loaded(){ + stepper = this; + this->register_for_event(ON_BLOCK_BEGIN); + this->register_for_event(ON_BLOCK_END); + this->register_for_event(ON_GCODE_EXECUTE); + this->register_for_event(ON_PLAY); + this->register_for_event(ON_PAUSE); + + // Get onfiguration + this->on_config_reload(this); + + // Acceleration ticker + //this->kernel->slow_ticker->set_frequency(this->acceleration_ticks_per_second/10); + this->kernel->slow_ticker->attach( this->acceleration_ticks_per_second, this, &Stepper::trapezoid_generator_tick ); + + // Initiate main_interrupt timer and step reset timer + this->kernel->step_ticker->attach( this, &Stepper::main_interrupt ); + this->kernel->step_ticker->reset_attach( this, &Stepper::reset_step_pins ); + +} + +// Get configuration from the config file +void Stepper::on_config_reload(void* argument){ + + this->microseconds_per_step_pulse = this->kernel->config->value(microseconds_per_step_pulse_checksum )->by_default(5 )->as_number(); + this->acceleration_ticks_per_second = this->kernel->config->value(acceleration_ticks_per_second_checksum)->by_default(100 )->as_number(); + this->minimum_steps_per_minute = this->kernel->config->value(minimum_steps_per_minute_checksum )->by_default(1200 )->as_number(); + this->base_stepping_frequency = this->kernel->config->value(base_stepping_frequency_checksum )->by_default(100000)->as_number(); + this->alpha_step_pin = this->kernel->config->value(alpha_step_pin_checksum )->by_default("1.21" )->as_pin()->as_output(); + this->beta_step_pin = this->kernel->config->value(beta_step_pin_checksum )->by_default("1.23" )->as_pin()->as_output(); + this->gamma_step_pin = this->kernel->config->value(gamma_step_pin_checksum )->by_default("1.22!" )->as_pin()->as_output(); + this->alpha_dir_pin = this->kernel->config->value(alpha_dir_pin_checksum )->by_default("1.18" )->as_pin()->as_output(); + this->beta_dir_pin = this->kernel->config->value(beta_dir_pin_checksum )->by_default("1.20" )->as_pin()->as_output(); + this->gamma_dir_pin = this->kernel->config->value(gamma_dir_pin_checksum )->by_default("1.19" )->as_pin()->as_output(); + this->alpha_en_pin = this->kernel->config->value(alpha_en_pin_checksum )->by_default("0.4" )->as_pin()->as_output()->as_open_drain(); + this->beta_en_pin = this->kernel->config->value(beta_en_pin_checksum )->by_default("0.10" )->as_pin()->as_output()->as_open_drain(); + this->gamma_en_pin = this->kernel->config->value(gamma_en_pin_checksum )->by_default("0.19" )->as_pin()->as_output()->as_open_drain(); + + + // TODO :Â This is supposed to be done by gcodes + this->alpha_en_pin->set(0); + this->beta_en_pin->set(0); + this->gamma_en_pin->set(0); + + + // Set the Timer interval for Match Register 1, + this->kernel->step_ticker->set_reset_delay( this->microseconds_per_step_pulse / 1000000 ); + this->kernel->step_ticker->set_frequency( this->base_stepping_frequency ); +} + +// When the play/pause button is set to pause, or a module calls the ON_PAUSE event +void Stepper::on_pause(void* argument){ + this->paused = true; +} + +// When the play/pause button is set to play, or a module calls the ON_PLAY event +void Stepper::on_play(void* argument){ + // TODO:Â Re-compute the whole queue for a cold-start + this->paused = false; +} + +void Stepper::on_gcode_execute(void* argument){ + Gcode* gcode = static_cast<Gcode*>(argument); + + if( gcode->has_letter('M')){ + int code = (int) gcode->get_value('M'); + if( code == 84 ){ + this->alpha_en_pin->set(0); + this->beta_en_pin->set(0); + this->gamma_en_pin->set(0); + } + } +} + +// AÂ new block is popped from the queue +void Stepper::on_block_begin(void* argument){ + Block* block = static_cast<Block*>(argument); + + // The stepper does not care about 0-blocks + if( block->millimeters == 0.0 ){ return; } + + // Mark the new block as of interrest to us + block->take(); + + // Setup + for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){ this->counters[stpr] = 0; this->stepped[stpr] = 0; } + this->step_events_completed = 0; + + this->current_block = block; + + // This is just to save computing power and not do it every step + this->update_offsets(); + + // Setup acceleration for this block + this->trapezoid_generator_reset(); + +} + +// Current block is discarded +void Stepper::on_block_end(void* argument){ + Block* block = static_cast<Block*>(argument); + this->current_block = NULL; //stfu ! +} + +// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Smoothie. It is executed at the rate set with +// config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. +inline uint32_t Stepper::main_interrupt(uint32_t dummy){ + if( this->paused ){ return 0; } + + // Step dir pins first, then step pinse, stepper drivers like to know the direction before the step signal comes in + this->alpha_dir_pin->set( ( this->out_bits >> 0 ) & 1 ); + this->beta_dir_pin->set( ( this->out_bits >> 1 ) & 1 ); + this->gamma_dir_pin->set( ( this->out_bits >> 2 ) & 1 ); + this->alpha_step_pin->set( ( this->out_bits >> 3 ) & 1 ); + this->beta_step_pin->set( ( this->out_bits >> 4 ) & 1 ); + this->gamma_step_pin->set( ( this->out_bits >> 5 ) & 1 ); + + if( this->current_block != NULL ){ + // Set bits for direction and steps + this->out_bits = this->current_block->direction_bits; + for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){ + this->counters[stpr] += this->counter_increment; + if( this->counters[stpr] > this->offsets[stpr] && this->stepped[stpr] < this->current_block->steps[stpr] ){ + this->counters[stpr] -= this->offsets[stpr] ; + this->stepped[stpr]++; + this->out_bits |= (1 << (stpr+3) ); + } + } + // If current block is finished, reset pointer + this->step_events_completed += this->counter_increment; + if( this->step_events_completed >= this->current_block->steps_event_count<<16 ){ + if( this->stepped[ALPHA_STEPPER] == this->current_block->steps[ALPHA_STEPPER] && this->stepped[BETA_STEPPER] == this->current_block->steps[BETA_STEPPER] && this->stepped[GAMMA_STEPPER] == this->current_block->steps[GAMMA_STEPPER] ){ + if( this->current_block != NULL ){ + this->current_block->release(); + } + } + } + }else{ + this->out_bits = 0; + } + +} + +// We compute this here instead of each time in the interrupt +void Stepper::update_offsets(){ + for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){ + this->offsets[stpr] = (int)floor((float)((float)(1<<16)*(float)((float)this->current_block->steps_event_count / (float)this->current_block->steps[stpr]))); + } +} + +// This is called ACCELERATION_TICKS_PER_SECOND times per second by the step_event +// interrupt. It can be assumed that the trapezoid-generator-parameters and the +// current_block stays untouched by outside handlers for the duration of this function call. +uint32_t Stepper::trapezoid_generator_tick( uint32_t dummy ) { + if(this->current_block && !this->trapezoid_generator_busy && !this->paused ) { + if(this->step_events_completed < this->current_block->accelerate_until<<16) { + this->trapezoid_adjusted_rate += this->current_block->rate_delta; + if (this->trapezoid_adjusted_rate > this->current_block->nominal_rate ) { + this->trapezoid_adjusted_rate = this->current_block->nominal_rate; + } + this->set_step_events_per_minute(this->trapezoid_adjusted_rate); + } else if (this->step_events_completed >= this->current_block->decelerate_after<<16) { + // NOTE: We will only reduce speed if the result will be > 0. This catches small + // rounding errors that might leave steps hanging after the last trapezoid tick. + if (this->trapezoid_adjusted_rate > double(this->current_block->rate_delta) * 1.5) { + this->trapezoid_adjusted_rate -= this->current_block->rate_delta; + }else{ + this->trapezoid_adjusted_rate = double(this->current_block->rate_delta) * 1.5; + //this->trapezoid_adjusted_rate = floor(double(this->trapezoid_adjusted_rate / 2 )); + //this->kernel->serial->printf("over!\r\n"); + } + if (this->trapezoid_adjusted_rate < this->current_block->final_rate ) { + this->trapezoid_adjusted_rate = this->current_block->final_rate; + } + this->set_step_events_per_minute(this->trapezoid_adjusted_rate); + } else { + // Make sure we cruise at exactly nominal rate + if (this->trapezoid_adjusted_rate != this->current_block->nominal_rate) { + this->trapezoid_adjusted_rate = this->current_block->nominal_rate; + this->set_step_events_per_minute(this->trapezoid_adjusted_rate); + } + } + } +} + +// Initializes the trapezoid generator from the current block. Called whenever a new +// block begins. +void Stepper::trapezoid_generator_reset(){ + this->trapezoid_adjusted_rate = this->current_block->initial_rate; + this->trapezoid_tick_cycle_counter = 0; + // Because this can be called directly from the main loop, it could be interrupted by the acceleration ticker, and that would be bad, so we use a flag + this->trapezoid_generator_busy = true; + this->set_step_events_per_minute(this->trapezoid_adjusted_rate); + this->trapezoid_generator_busy = false; +} + +void Stepper::set_step_events_per_minute( double steps_per_minute ){ + + // We do not step slower than this + steps_per_minute = max(steps_per_minute, this->minimum_steps_per_minute); + + // The speed factor is the factor by which we must multiply the minimal step frequency to reach the maximum step frequency + // The higher, the smoother the movement will be, but the closer the maximum and minimum frequencies are, the smaller the factor is + // speed_factor = base_stepping_frequency / steps_per_second + // counter_increment = 1 / speed_factor ( 1 is 1<<16 because we do fixed point ) + this->counter_increment = int(floor(double(1<<16)/double(this->base_stepping_frequency / (steps_per_minute/60L)))); + + this->kernel->call_event(ON_SPEED_CHANGE, this); + +} + +uint32_t Stepper::reset_step_pins(uint32_t dummy){ + this->alpha_step_pin->set(0); + this->beta_step_pin->set(0); + this->gamma_step_pin->set(0); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/Stepper.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,83 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef STEPPER_H +#define STEPPER_H +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "Planner.h" +#include "Block.h" + +#define microseconds_per_step_pulse_checksum 42333 +#define acceleration_ticks_per_second_checksum 25075 +#define minimum_steps_per_minute_checksum 9003 +#define base_stepping_frequency_checksum 21918 +#define alpha_step_pin_checksum 11468 +#define beta_step_pin_checksum 22114 +#define gamma_step_pin_checksum 1225 +#define alpha_dir_pin_checksum 55887 +#define beta_dir_pin_checksum 28644 +#define gamma_dir_pin_checksum 46412 +#define alpha_en_pin_checksum 35042 +#define beta_en_pin_checksum 34680 +#define gamma_en_pin_checksum 26335 + + +class Stepper : public Module { + public: + Stepper(); + virtual void on_module_loaded(); + virtual void on_config_reload(void* argument); + virtual void on_block_begin(void* argument); + virtual void on_block_end(void* argument); + virtual void on_gcode_execute(void* argument); + virtual void on_play(void* argument); + virtual void on_pause(void* argument); + uint32_t main_interrupt(uint32_t dummy); + void trapezoid_generator_reset(); + void set_step_events_per_minute(double steps_per_minute); + uint32_t trapezoid_generator_tick(uint32_t dummy); + uint32_t reset_step_pins(uint32_t dummy); + void update_offsets(); + int config_step_timer( int cycles ); + + Block* current_block; + int counters[3]; + int stepped[3]; + int offsets[3]; + float counter_alpha; + float counter_beta; + float counter_gamma; + int step_events_completed; + unsigned int out_bits; + double trapezoid_adjusted_rate; + int trapezoid_tick_cycle_counter; + int cycles_per_step_event; + bool trapezoid_generator_busy; + int microseconds_per_step_pulse; + int acceleration_ticks_per_second; + int divider; + int minimum_steps_per_minute; + int base_stepping_frequency; + Pin* alpha_step_pin; + Pin* beta_step_pin; + Pin* gamma_step_pin; + Pin* alpha_dir_pin; + Pin* beta_dir_pin; + Pin* gamma_dir_pin; + Pin* alpha_en_pin; + Pin* beta_en_pin; + Pin* gamma_en_pin; + unsigned short step_bits[3]; + int counter_increment; + bool paused; +}; + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/arm_solutions/BaseSolution.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,6 @@ +#include "BaseSolution.h" + +BaseSolution::BaseSolution(){} + +void BaseSolution::millimeters_to_steps( double millimeters[], int steps[] ){} +void BaseSolution::steps_to_millimeters( int steps[], double millimeters[] ){}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/arm_solutions/BaseSolution.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,12 @@ +// Base class for an arm solution, only usefull for inheritence. http://en.wikipedia.org/wiki/Arm_solution +#ifndef BASESOLUTION_H +#define BASESOLUTION_H + +class BaseSolution { + public: + BaseSolution(); + virtual void millimeters_to_steps( double millimeters[], int steps[] ); + virtual void steps_to_millimeters( int steps[], double millimeters[] ); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/arm_solutions/CartesianSolution.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,16 @@ +#include "CartesianSolution.h" +#include <math.h> + +CartesianSolution::CartesianSolution(Config* passed_config) : config(passed_config){ + this->alpha_steps_per_mm = this->config->value(alpha_steps_per_mm_checksum)->as_number(); + this->beta_steps_per_mm = this->config->value( beta_steps_per_mm_checksum)->as_number(); + this->gamma_steps_per_mm = this->config->value(gamma_steps_per_mm_checksum)->as_number(); +} + +void CartesianSolution::millimeters_to_steps( double millimeters[], int steps[] ){ + steps[ALPHA_STEPPER] = floor( millimeters[X_AXIS] * this->alpha_steps_per_mm +0.5); + steps[BETA_STEPPER ] = floor( millimeters[Y_AXIS] * this->beta_steps_per_mm +0.5); + steps[GAMMA_STEPPER] = floor( millimeters[Z_AXIS] * this->gamma_steps_per_mm +0.5); +} + +void CartesianSolution::steps_to_millimeters( int steps[], double millimeters[] ){}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/robot/arm_solutions/CartesianSolution.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,31 @@ +#ifndef CARTESIANSOLUTION_H +#define CARTESIANSOLUTION_H +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "BaseSolution.h" +#include "libs/nuts_bolts.h" + +#include "libs/Config.h" + +#define alpha_steps_per_mm_checksum 46458 +#define beta_steps_per_mm_checksum 13840 +#define gamma_steps_per_mm_checksum 33143 + +class CartesianSolution : public BaseSolution { + public: + CartesianSolution(Config* passed_config); + virtual void millimeters_to_steps( double millimeters[], int steps[] ); + virtual void steps_to_millimeters( int steps[], double millimeters[] ); + + Config* config; + double alpha_steps_per_mm; + double beta_steps_per_mm; + double gamma_steps_per_mm; +}; + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/extruder/Extruder.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,285 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "modules/robot/Player.h" +#include "modules/robot/Block.h" +#include "modules/tools/extruder/Extruder.h" + +#define extruder_step_pin_checksum 40763 +#define extruder_dir_pin_checksum 57277 +#define extruder_en_pin_checksum 8017 + +Extruder::Extruder() { + this->absolute_mode = true; + this->direction = 1; + this->acceleration_lock = false; + this->step_counter = 0; + this->counter_increment = 0; + this->paused = false; +} + +void Extruder::on_module_loaded() { + + // Do not do anything if not enabledd + if( this->kernel->config->value( extruder_module_enable_checksum )->by_default(false)->as_bool() == false ){ return; } + + // Settings + this->on_config_reload(this); + + // We work on the same Block as Stepper, so we need to know when it gets a new one and drops one + this->register_for_event(ON_BLOCK_BEGIN); + this->register_for_event(ON_BLOCK_END); + this->register_for_event(ON_GCODE_EXECUTE); + this->register_for_event(ON_PLAY); + this->register_for_event(ON_PAUSE); + + // Start values + this->start_position = 0; + this->target_position = 0; + this->current_position = 0; + this->current_block = NULL; + this->mode = OFF; + + // Update speed every *acceleration_ticks_per_second* + // TODO:Â Make this an independent setting + this->kernel->slow_ticker->attach( this->kernel->stepper->acceleration_ticks_per_second , this, &Extruder::acceleration_tick ); + + // Initiate main_interrupt timer and step reset timer + this->kernel->step_ticker->attach( this, &Extruder::stepping_tick ); + this->kernel->step_ticker->reset_attach( this, &Extruder::reset_step_pin ); + +} + +// Get config +void Extruder::on_config_reload(void* argument){ + this->microseconds_per_step_pulse = this->kernel->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); + this->steps_per_millimeter = this->kernel->config->value(extruder_steps_per_mm_checksum )->by_default(1)->as_number(); + this->feed_rate = this->kernel->config->value(default_feed_rate_checksum )->by_default(1)->as_number(); + this->acceleration = this->kernel->config->value(acceleration_checksum )->by_default(1)->as_number(); + + this->step_pin = this->kernel->config->value(extruder_step_pin_checksum )->by_default("1.22" )->as_pin()->as_output(); + this->dir_pin = this->kernel->config->value(extruder_dir_pin_checksum )->by_default("1.19" )->as_pin()->as_output(); + this->en_pin = this->kernel->config->value(extruder_en_pin_checksum )->by_default("0.19" )->as_pin()->as_output(); +} + + +// When the play/pause button is set to pause, or a module calls the ON_PAUSE event +void Extruder::on_pause(void* argument){ + this->paused = true; +} + +// When the play/pause button is set to play, or a module calls the ON_PLAY event +void Extruder::on_play(void* argument){ + this->paused = false; +} + + + +// Compute extrusion speed based on parameters and gcode distance of travel +void Extruder::on_gcode_execute(void* argument){ + Gcode* gcode = static_cast<Gcode*>(argument); + + // Absolute/relative mode + if( gcode->has_letter('M')){ + int code = (int) gcode->get_value('M'); + if( code == 82 ){ this->absolute_mode = true; } + if( code == 83 ){ this->absolute_mode = false; } + if( code == 84 ){ this->en_pin->set(0); } + } + + // The mode is OFF by default, and SOLO or FOLLOW only if we need to extrude + this->mode = OFF; + + if( gcode->has_letter('G') ){ + // G92:Â Reset extruder position + if( gcode->get_value('G') == 92 ){ + if( gcode->has_letter('E') ){ + this->current_position = gcode->get_value('E'); + this->target_position = this->current_position; + this->start_position = this->current_position; + } + }else{ + // Extrusion length from 'G' Gcode + if( gcode->has_letter('E' )){ + // Get relative extrusion distance depending on mode ( in absolute mode we must substract target_position ) + double relative_extrusion_distance = gcode->get_value('E'); + if( this->absolute_mode == true ){ relative_extrusion_distance = relative_extrusion_distance - this->target_position; } + + // If the robot is moving, we follow it's movement, otherwise, we move alone + if( fabs(gcode->millimeters_of_travel) < 0.0001 ){ // With floating numbers, we can have 0 != 0 ... beeeh + this->mode = SOLO; + this->travel_distance = relative_extrusion_distance; + if( gcode->has_letter('F') ){ this->feed_rate = gcode->get_value('F'); } + }else{ + this->mode = FOLLOW; + // We move proportionally to the robot's movement + this->travel_ratio = relative_extrusion_distance / gcode->millimeters_of_travel; + } + + this->en_pin->set(1); + } + } + } + +} + +// When a new block begins, either follow the robot, or step by ourselves ( or stay back and do nothing ) +void Extruder::on_block_begin(void* argument){ + Block* block = static_cast<Block*>(argument); + if( this->mode == SOLO ){ + // In solo mode we take the block so we can move even if the stepper has nothing to do + block->take(); + this->current_block = block; + this->start_position = this->target_position; + this->target_position = this->start_position + this->travel_distance ; + this->travel_ratio = 0.2; // TODOÂ :Â Make a real acceleration thing + if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; } + this->set_speed(int(floor((this->feed_rate/60)*this->steps_per_millimeter)));//Speed in steps per second + }else if( this->mode == FOLLOW ){ + // In non-solo mode, we just follow the stepper module + this->current_block = block; + this->start_position = this->target_position; + this->target_position = this->start_position + ( this->current_block->millimeters * this->travel_ratio ); + if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; } + this->acceleration_tick(0); + } + +} + +// When a block ends, pause the stepping interrupt +void Extruder::on_block_end(void* argument){ + Block* block = static_cast<Block*>(argument); + this->current_block = NULL; +} + +// Called periodically to change the speed to match acceleration or to match the speed of the robot +uint32_t Extruder::acceleration_tick(uint32_t dummy){ + + // Avoid trying to work when we really shouldn't ( between blocks or re-entry ) + if( this->current_block == NULL || this->acceleration_lock || this->paused ){ return 0; } + this->acceleration_lock = true; + + // In solo mode, we mode independently from the robot + if( this->mode == SOLO ){ + // TODOÂ :Â Do real acceleration here + this->travel_ratio += 0.03; + if( this->travel_ratio > 1 ){ this->travel_ratio = 1; } + this->set_speed( int(floor(((this->feed_rate/60)*this->steps_per_millimeter)*this->travel_ratio)) ); // Speed in steps per second + + // In follow mode we match the speed of the robot, + eventually advance + }else if( this->mode == FOLLOW ){ + Stepper* stepper = this->kernel->stepper; // Just for convenience + + // Strategy : + // * Find where in the block will the stepper be at the next tick ( if the block will have ended then, don't change speed ) + // * Find what position this is for us + // * Find what speed we must go at to be at that position for the next acceleration tick + // TODOÂ :Â This works, but PLEASEÂ PLEASEÂ PLEASEÂ if you know a better way to do it, do it better, I don't find this elegant at all, it's just the best IÂ could think of + // UPDATE:Â Yes, this sucks, I have ideas on how to do it better. If this is really bugging you, open a ticket and I'll make it a priority + + int ticks_forward = 3; + // We need to take those values here, and then use those instead of the live values, because using the live values inside the loop can break things ( infinite loops etc ... ) + double next_stepper_rate = stepper->trapezoid_adjusted_rate; + double step_events_completed = (double(double(stepper->step_events_completed)/double(1<<16))); + double position = ( this->current_position - this->start_position ) * this->direction ; + double length = fabs( this->start_position - this->target_position ); + double last_ratio = -1; + + // Do the startegy above, but if it does not work, look a bit further and try again, and again ... + while(1){ + + // Find the position where we should be at the next tick + double next_ratio = double( step_events_completed + ( next_stepper_rate / 60 / ((double(stepper->acceleration_ticks_per_second)/ticks_forward)) ) ) / double( this->current_block->steps_event_count ); + double next_relative_position = ( length * next_ratio ); + + // Advance + // TODO:Â Proper advance configuration + double advance = double(next_stepper_rate) * ( 0.00001 * 0.15 ) * 0.4 ; + //double advance = 0; + next_relative_position += ( advance ); + + // TODOÂ :Â all of those "if->return" is very hacky, we should do the math in a way where most of those don't happen, but that requires doing tons of drawing ... + if( last_ratio == next_ratio ){ this->acceleration_lock = false; return 0; }else{ last_ratio = next_ratio; } + if( next_ratio == 0 || next_ratio > 1 ){ this->acceleration_lock = false; return 0; } + if( ticks_forward > 1000 ){ this->acceleration_lock = false; return 0; } // This is very ugly + + // Hack :Â We have not looked far enough, we compute how far ahead we must look to get a relevant value + if( position > next_relative_position ){ + double far_back = position - next_relative_position; + double far_back_ratio = far_back / length; + double move_duration = double( this->current_block->steps_event_count ) / ( double(next_stepper_rate) / 60 ) ; + double ticks_in_a_move = floor( stepper->acceleration_ticks_per_second * move_duration +0.5); + double ratio_per_tick = 1 / ticks_in_a_move; + double ticks_to_equilibrium = ceil(far_back_ratio / ratio_per_tick) + 1; + ticks_forward += ticks_to_equilibrium; + // Because this is a loop, and we can be interrupted by the stepping interrupt, if that interrupt changes block, the new block may not be solo, and we may get trapped into an infinite loop + if( this->mode != FOLLOW ){ this->acceleration_lock = false; return 0; } + continue; + } + + // Finally, compute the speed to get to that next position + double next_absolute_position = this->start_position + ( this->direction * next_relative_position ); + double steps_to_next_tick = ( next_relative_position - position ) * this->steps_per_millimeter; + double speed_to_next_tick = steps_to_next_tick / ( 1 / double(double(this->kernel->stepper->acceleration_ticks_per_second) / ticks_forward) ); + + // Change stepping speed + this->set_speed( speed_to_next_tick ); + + this->acceleration_lock = false; + return 0; + } + } + + this->acceleration_lock = false; + return 0; +} + +// Convenience function to set stepping speed +void Extruder::set_speed( int steps_per_second ){ + + if( steps_per_second < 10 ){ steps_per_second = 10; } + + // TODOÂ :Â Proper limit config value + if( steps_per_second > (this->feed_rate*double(this->steps_per_millimeter))/60 ){ + steps_per_second = (this->feed_rate*double(this->steps_per_millimeter))/60; + } + + this->counter_increment = int(floor(double(1<<16)/double(this->kernel->stepper->base_stepping_frequency / steps_per_second))); + +} + +inline uint32_t Extruder::stepping_tick(uint32_t dummy){ + if( this->paused ){ return 0; } + + this->step_counter += this->counter_increment; + if( this->step_counter > 1<<16 ){ + this->step_counter -= 1<<16; + + // If we still have steps to do + // TODO:Â Step using the same timer as the robot, and count steps instead of absolute float position + if( ( this->current_position < this->target_position && this->direction == 1 ) || ( this->current_position > this->target_position && this->direction == -1 ) ){ + this->current_position += (double(double(1)/double(this->steps_per_millimeter)))*double(this->direction); + this->dir_pin->set((this->direction > 0) ? 1 : 0); + this->step_pin->set(1); + }else{ + // Move finished + if( this->mode == SOLO && this->current_block != NULL ){ + // In follow mode, the robot takes and releases the block, in solo mode we do + this->current_block->release(); + } + } + } + return 0; +} + +uint32_t Extruder::reset_step_pin(uint32_t dummy){ + this->step_pin->set(0); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/extruder/Extruder.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,74 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + + +#ifndef EXTURDER_MODULE_H +#define EXTRUDER_MODULE_H + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "modules/robot/Block.h" + +#define microseconds_per_step_pulse_checksum 42333 +#define extruder_module_enable_checksum 6183 +#define extruder_steps_per_mm_checksum 58088 +#define default_feed_rate_checksum 53183 +#define acceleration_checksum 60356 + +#define OFF 0 +#define SOLO 1 +#define FOLLOW 2 + +class Extruder : public Module{ + public: + Extruder(); + virtual void on_module_loaded(); + virtual void on_config_reload(void* argument); + virtual void on_gcode_execute(void* argument); + virtual void on_block_begin(void* argument); + virtual void on_block_end(void* argument); + virtual void on_play(void* argument); + virtual void on_pause(void* argument); + void set_speed(int steps_per_second); + uint32_t acceleration_tick(uint32_t dummy); + uint32_t stepping_tick(uint32_t dummy); + uint32_t reset_step_pin(uint32_t dummy); + + Pin* step_pin; // Step pin for the stepper driver + Pin* dir_pin; // Dir pin for the stepper driver + Pin* en_pin; + + double start_position; // Start point ( in steps ) for the current move + double target_position; // End point ( in steps ) for the current move + double current_position; // Current point ( in steps ) for the current move, incremented every time a step is outputed + Block* current_block; // Current block we are stepping, same as Stepper's one + int microseconds_per_step_pulse; // Pulse duration for step pulses + double steps_per_millimeter; // Steps to travel one millimeter + double feed_rate; // + double acceleration; // + + int counter_increment; + int step_counter; + + bool solo_mode; + double travel_ratio; + double travel_distance; + bool absolute_mode; + + int direction; + + bool debug; + int debug_count; + + char mode; + bool acceleration_lock; + + bool paused; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/laser/Laser.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,73 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "modules/communication/utils/Gcode.h" +#include "modules/robot/Stepper.h" +#include "Laser.h" +#include "libs/nuts_bolts.h" + +Laser::Laser(PinName pin) : laser_pin(pin){ + this->laser_pin.period_us(10); +} + +void Laser::on_module_loaded() { + if( !this->kernel->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ){ return; } + this->register_for_event(ON_GCODE_EXECUTE); + this->register_for_event(ON_SPEED_CHANGE); + this->register_for_event(ON_PLAY); + this->register_for_event(ON_PAUSE); + this->register_for_event(ON_BLOCK_BEGIN); + this->register_for_event(ON_BLOCK_END); +} + +// Turn laser off laser at the end of a move +void Laser::on_block_end(void* argument){ + this->laser_pin = 0; +} + +// Set laser power at the beginning of a block +void Laser::on_block_begin(void* argument){ + this->set_proportional_power(); +} + +// When the play/pause button is set to pause, or a module calls the ON_PAUSE event +void Laser::on_pause(void* argument){ + this->laser_pin = 0; +} + +// When the play/pause button is set to play, or a module calls the ON_PLAY event +void Laser::on_play(void* argument){ + this->set_proportional_power(); +} + +// Turn laser on/off depending on received GCodes +void Laser::on_gcode_execute(void* argument){ + Gcode* gcode = static_cast<Gcode*>(argument); + this->laser_on = false; + if( gcode->has_letter('G' )){ + int code = gcode->get_value('G'); + if( code == 0 ){ // G0 + this->laser_pin = 0; + this->laser_on = false; + }else if( code >= 1 && code <= 3 ){ // G1, G2, G3 + this->laser_on = true; + } + } +} + +// We follow the stepper module here, so speed must be proportional +void Laser::on_speed_change(void* argument){ + this->set_proportional_power(); +} + +void Laser::set_proportional_power(){ + if( this->laser_on && this->kernel->stepper->current_block ){ + this->laser_pin = double(this->kernel->stepper->trapezoid_adjusted_rate)/double(this->kernel->stepper->current_block->nominal_rate); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/laser/Laser.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,51 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef LASER_MODULE_H +#define LASER_MODULE_H + +#include "libs/Module.h" +#include "PwmOut.h" // mbed.h lib +#include "libs/Kernel.h" +#include "modules/communication/utils/Gcode.h" + + +#define laser_module_enable_checksum 35529 + +class Laser : public Module{ + public: + Laser(PinName pin); + virtual void on_module_loaded(); + virtual void on_block_end(void* argument); + virtual void on_block_begin(void* argument); + virtual void on_play(void* argument); + virtual void on_pause(void* argument); + virtual void on_gcode_execute(void* argument); + virtual void on_speed_change(void* argument); + void set_proportional_power(); + + mbed::PwmOut laser_pin; // PWM output to regulate the laser power + bool laser_on; // Laser status +}; + + + + + + + + + + + + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/spindle/Spindle.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,81 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include "modules/communication/utils/Gcode.h" +#include "modules/robot/Stepper.h" +#include "Spindle.h" +#include "libs/nuts_bolts.h" + +Spindle::Spindle(){ + +} + +void Spindle::on_module_loaded() { + if( !this->kernel->config->value( spindle_module_enable_checksum )->by_default(false)->as_bool() ){ return; } + + // Settings + this->on_config_reload(this); + + this->register_for_event(ON_GCODE_EXECUTE); + this->register_for_event(ON_PLAY); + this->register_for_event(ON_PAUSE); + this->register_for_event(ON_BLOCK_BEGIN); + this->register_for_event(ON_BLOCK_END); +} + + +// Get config +void Spindle::on_config_reload(void* argument){ + this->spindle_pin= this->kernel->config->value(spindle_pin_checksum)->by_default("0.4" )->as_pin()->as_output(); +} + +// Turn spindle off spindle at the end of a move +void Spindle::on_block_end(void* argument){ + //this->spindle_pin->set(0); +} + +// Set spindle power at the beginning of a block +void Spindle::on_block_begin(void* argument){ + //this->set_spindle(); +} + +// When the play/pause button is set to pause, or a module calls the ON_PAUSE event +void Spindle::on_pause(void* argument){ + this->spindle_pin->set(0); +} + +// When the play/pause button is set to play, or a module calls the ON_PLAY event +void Spindle::on_play(void* argument){ + this->set_spindle(); +} + +// Turn spindle on/off depending on received GCodes +void Spindle::on_gcode_execute(void* argument){ + Gcode* gcode = static_cast<Gcode*>(argument); + this->spindle_on = false; + if( gcode->has_letter('G' )){ + int code = gcode->get_value('G'); + if( code == 0 ){ // G0 + this->kernel->serial->printf("off\r\n"); + this->spindle_pin->set(0); + this->spindle_on = false; + }else if( code >= 1 && code <= 3 ){ // G1, G2, G3 + this->kernel->serial->printf("on\r\n"); + this->spindle_pin->set(1); + this->spindle_on = true; + } + } +} + + +void Spindle::set_spindle(){ + if( this->spindle_on && this->kernel->stepper->current_block ){ + this->spindle_pin->set(1); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/spindle/Spindle.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,51 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef SPINDLE_MODULE_H +#define SPINDLE_MODULE_H + +#include "libs/Module.h" +#include "mbed.h" +#include "libs/Kernel.h" +#include "modules/communication/utils/Gcode.h" + +#define spindle_module_enable_checksum 43962 +#define spindle_pin_checksum 54937 + +class Spindle : public Module{ + public: + Spindle(); + virtual void on_module_loaded(); + virtual void on_config_reload(void* argument); + virtual void on_block_end(void* argument); + virtual void on_block_begin(void* argument); + virtual void on_play(void* argument); + virtual void on_pause(void* argument); + virtual void on_gcode_execute(void* argument); + private: + void set_spindle(); + bool spindle_on; // Spindle status + Pin* spindle_pin; +}; + + + + + + + + + + + + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/temperaturecontrol/TemperatureControl.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,187 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +// TODOÂ :Â THISÂ FILEÂ ISÂ LAME, MUSTÂ BEÂ MADEÂ MUCHÂ BETTER + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include <math.h> +#include "TemperatureControl.h" +#include "libs/Pin.h" + +TemperatureControl::TemperatureControl(){} + +TemperatureControl::TemperatureControl(uint16_t name){ + this->name_checksum = name; + this->error_count = 0; +} + +void TemperatureControl::on_module_loaded(){ + + // We start not desiring any temp + this->desired_adc_value = UNDEFINED; + + // Settings + this->on_config_reload(this); + + this->acceleration_factor = 10; + + this->kernel->slow_ticker->attach( 20, this, &TemperatureControl::thermistor_read_tick ); + + // Register for events + this->register_for_event(ON_GCODE_EXECUTE); + this->register_for_event(ON_MAIN_LOOP); + +} + +void TemperatureControl::on_main_loop(void* argument){ } + +// Get configuration from the config file +void TemperatureControl::on_config_reload(void* argument){ + + this->readings_per_second = this->kernel->config->value(temperature_control_checksum, this->name_checksum, readings_per_second_checksum)->by_default(5)->as_number(); + + // Values are here : http://reprap.org/wiki/Thermistor + this->r0 = 100000; + this->t0 = 25; + this->beta = 4066; + this->vadc = 3.3; + this->vcc = 3.3; + this->r1 = 0; + this->r2 = 4700; + + // Preset values for various common types of thermistors + ConfigValue* thermistor = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_checksum); + if( thermistor->value.compare("EPCOS100K" ) == 0 ){ // Default + }else if( thermistor->value.compare("RRRF100K" ) == 0 ){ this->beta = 3960; + }else if( thermistor->value.compare("RRRF10K" ) == 0 ){ this->beta = 3964; this->r0 = 10000; this->r1 = 680; this->r2 = 1600; + }else if( thermistor->value.compare("Honeywell100K") == 0 ){ this->beta = 3974; + }else if( thermistor->value.compare("Semitec" ) == 0 ){ this->beta = 4267; } + + // Preset values are overriden by specified values + this->r0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r0_checksum )->by_default(100000)->as_number(); // Stated resistance eg. 100K + this->t0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, t0_checksum )->by_default(25 )->as_number() + 273.15; // Temperature at stated resistance, eg. 25C + this->beta = this->kernel->config->value(temperature_control_checksum, this->name_checksum, beta_checksum)->by_default(4066 )->as_number(); // Thermistor beta rating. See http://reprap.org/bin/view/Main/MeasuringThermistorBeta + this->vadc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vadc_checksum)->by_default(3.3 )->as_number(); // ADC Reference + this->vcc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vcc_checksum )->by_default(3.3 )->as_number(); // Supply voltage to potential divider + this->r1 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r1_checksum )->by_default(0 )->as_number(); + this->r2 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r2_checksum )->by_default(4700 )->as_number(); + + // Thermistor math + this->k = this->r0 * exp( -this->beta / this->t0 ); + if( r1 > 0 ){ this->vs = r1 * this->vcc / ( r1 + r2 ); this->rs = r1 * r2 / ( r1 + r2 ); }else{ this->vs = this->vcc; this->rs = r2; } + + // Thermistor pin for ADC readings + this->thermistor_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_pin_checksum )->required()->as_pin(); + this->kernel->adc->enable_pin(this->thermistor_pin); + + // Heater pin + this->heater_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_pin()->as_output(); + this->heater_pin->set(0); + +} + +void TemperatureControl::on_gcode_execute(void* argument){ + Gcode* gcode = static_cast<Gcode*>(argument); + + // Set temperature without waiting + if( gcode->has_letter('M') && gcode->get_value('M') == 104 && gcode->has_letter('S') ){ + this->set_desired_temperature(gcode->get_value('S')); + } + + // Set temperature and wait + if( gcode->has_letter('M') && gcode->get_value('M') == 109 && gcode->has_letter('S') ){ + this->set_desired_temperature(gcode->get_value('S')); + + // Pause + this->kernel->pauser->take(); + this->waiting = true; + + } + + // Get temperature + if( gcode->has_letter('M') && gcode->get_value('M') == 105 ){ + gcode->stream->printf("get temperature: %f current:%f target:%f \r\n", this->get_temperature(), this->new_thermistor_reading(), this->desired_adc_value ); + } +} + +void TemperatureControl::set_desired_temperature(double desired_temperature){ + this->desired_adc_value = this->temperature_to_adc_value(desired_temperature); +} + +double TemperatureControl::get_temperature(){ + double temp = this->new_thermistor_reading() ; + return this->adc_value_to_temperature( this->new_thermistor_reading() ); +} + +double TemperatureControl::adc_value_to_temperature(double adc_value){ + double v = adc_value * this->vadc; // Convert from 0-1 adc value to voltage + double r = this->rs * v / ( this->vs - v ); // Resistance of thermistor + return ( this->beta / log( r / this->k )) - 273.15; +} + +double TemperatureControl::temperature_to_adc_value(double temperature){ + double r = this->r0 * exp( this->beta * ( 1 / (temperature + 273.15) -1 / this->t0 ) ); // Resistance of the thermistor + double v = this->vs * r / ( this->rs + r ); // Voltage at the potential divider + return v / this->vadc * 1.00000; // The ADC reading +} + +uint32_t TemperatureControl::thermistor_read_tick(uint32_t dummy){ + if( this->desired_adc_value != UNDEFINED ){ + if( this->new_thermistor_reading() > this->desired_adc_value ){ + this->heater_pin->set(1); + }else{ + this->heater_pin->set(0); + if( this->waiting ){ + this->kernel->pauser->release(); + this->waiting = false; + } + } + } +} + +double TemperatureControl::new_thermistor_reading(){ + + double new_reading = double( double(this->kernel->adc->read(this->thermistor_pin) / double(1<<12) ) ); + + if( this->queue.size() < 15 ){ + this->queue.push_back( new_reading ); + return new_reading; + }else{ + double current_temp = this->average_adc_reading(); + double error = fabs(new_reading - current_temp); + if( error < 0.1 ){ + this->error_count = 0; + double test; + this->queue.pop_front(test); + this->queue.push_back( new_reading ); + }else{ + this->error_count++; + if( this->error_count > 4 ){ + double test; + this->queue.pop_front(test); + } + } + return current_temp; + } +} + + +double TemperatureControl::average_adc_reading(){ + double total; + int j=0; + int reading_index = this->queue.head; + while( reading_index != this->queue.tail ){ + j++; + total += this->queue.buffer[reading_index]; + reading_index = this->queue.next_block_index( reading_index ); + } + return total / j; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/temperaturecontrol/TemperatureControl.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,77 @@ +/* + this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl). + smoothie is free software: you can redistribute it and/or modify it under the terms of the gnu general public license as published by the free software foundation, either version 3 of the license, or (at your option) any later version. + smoothie is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. see the gnu general public license for more details. + you should have received a copy of the gnu general public license along with smoothie. if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef temperaturecontrol_h +#define temperaturecontrol_h + +#include "libs/Pin.h" +#include <math.h> + +#define UNDEFINED -1 + +#define thermistor_checksum 41045 +#define r0_checksum 5538 +#define readings_per_second_checksum 18645 +#define t0_checksum 6564 +#define beta_checksum 1181 +#define vadc_checksum 10911 +#define vcc_checksum 36157 +#define r1_checksum 5795 +#define r2_checksum 6052 +#define temperature_control_checksum 44054 +#define thermistor_pin_checksum 1788 +#define heater_pin_checksum 35619 + +class TemperatureControl : public Module { + public: + TemperatureControl(); + TemperatureControl(uint16_t name); + + virtual void on_module_loaded(); + virtual void on_main_loop(void* argument); + virtual void on_gcode_execute(void* argument); + virtual void on_config_reload(void* argument); + void set_desired_temperature(double desired_temperature); + double get_temperature(); + double adc_value_to_temperature(double adc_value); + double temperature_to_adc_value(double temperature); + uint32_t thermistor_read_tick(uint32_t dummy); + double new_thermistor_reading(); + double average_adc_reading(); + + double desired_adc_value; + double tail_adc_value; + double head_adc_value; + + // Thermistor computation settings + double r0; + double t0; + double r1; + double r2; + double beta; + double vadc; + double vcc; + double k; + double vs; + double rs; + + double acceleration_factor; + double readings_per_second; + + RingBuffer<double,16> queue; // Queue of Blocks + int error_count; + + uint16_t name_checksum; + + Pin* thermistor_pin; + Pin* heater_pin; + + bool waiting; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/temperaturecontrol/TemperatureControlPool.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,37 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libs/Module.h" +#include "libs/Kernel.h" +#include <math.h> +using namespace std; +#include <vector> +#include "TemperatureControlPool.h" +#include "TemperatureControl.h" + +TemperatureControlPool::TemperatureControlPool(){} + +void TemperatureControlPool::on_module_loaded(){ + + vector<uint16_t> modules; + this->kernel->config->get_module_list( &modules, temperature_control_checksum ); + + for( int i = 0; i < modules.size(); i++ ){ + // If module is enabled + if( this->kernel->config->value(temperature_control_checksum, modules[i], enable_checksum )->as_bool() == true ){ + TemperatureControl* controller = new TemperatureControl(modules[i]); + this->kernel->add_module(controller); + this->controllers.push_back( controller ); + } + } + +} + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/tools/temperaturecontrol/TemperatureControlPool.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,30 @@ +/* + this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl). + smoothie is free software: you can redistribute it and/or modify it under the terms of the gnu general public license as published by the free software foundation, either version 3 of the license, or (at your option) any later version. + smoothie is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. see the gnu general public license for more details. + you should have received a copy of the gnu general public license along with smoothie. if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef TEMPERATURECONTROLPOOL_H +#define TEMPERATURECONTROLPOOL_H + +#include "TemperatureControl.h" +#include <math.h> +using namespace std; +#include <vector> + +#define temperature_control_checksum 44054 +#define enable_checksum 29545 + +class TemperatureControlPool : public Module { + public: + TemperatureControlPool(); + + virtual void on_module_loaded(); + + vector<TemperatureControl*> controllers; +}; + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/configurator/Configurator.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,128 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "libs/Kernel.h" +#include "Configurator.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/SerialMessage.h" +#include "libs/StreamOutput.h" + + +void Configurator::on_module_loaded(){ + this->register_for_event(ON_CONSOLE_LINE_RECEIVED); +// this->register_for_event(ON_GCODE_EXECUTE); +// this->register_for_event(ON_MAIN_LOOP); +} + +// When a new line is received, check if it is a command, and if it is, act upon it +void Configurator::on_console_line_received( void* argument ){ + SerialMessage new_message = *static_cast<SerialMessage*>(argument); + string possible_command = new_message.message; + + // We don't compare to a string but to a checksum of that string, this saves some space in flash memory + uint16_t check_sum = get_checksum( possible_command.substr(0,possible_command.find_first_of(" \r\n")) ); // todo:Â put this method somewhere more convenient + + // Act depending on command + switch( check_sum ){ + case config_get_command_checksum: this->config_get_command( get_arguments(possible_command), new_message.stream ); break; + case config_set_command_checksum: this->config_set_command( get_arguments(possible_command), new_message.stream ); break; + case config_load_command_checksum: this->config_load_command( get_arguments(possible_command), new_message.stream ); break; + } +} + +// Process and respond to eeprom gcodes (M50x) +void Configurator::on_gcode_execute(void* argument){ + Gcode* gcode = static_cast<Gcode*>(argument); + if( gcode->has_letter('G') ){ + int code = gcode->get_value('G'); + switch( code ){ + } + } + else if( gcode->has_letter('M') ){ + int code = gcode->get_value('M'); + switch( code ){ + } + } +} + +void Configurator::on_main_loop(void* argument){} + +// Output a ConfigValue from the specified ConfigSource to the stream +void Configurator::config_get_command( string parameters, StreamOutput* stream ){ + string source = shift_parameter(parameters); + string setting = shift_parameter(parameters); + if (setting == "") { // output live setting + setting = source; + source = ""; + vector<uint16_t> setting_checksums = get_checksums( setting ); + ConfigValue* cv = this->kernel->config->value(setting_checksums); + string value = ""; + if(cv->found){ value = cv->as_string(); } + stream->printf( "live: %s is set to %s\r\n", setting.c_str(), value.c_str() ); + } else { // output setting from specified source + uint16_t source_checksum = get_checksum( source ); + vector<uint16_t> setting_checksums = get_checksums( setting ); + for(int i=0; i < this->kernel->config->config_sources.size(); i++){ + if( this->kernel->config->config_sources[i]->is_named(source_checksum) ){ + string value = this->kernel->config->config_sources[i]->read(setting_checksums); + stream->printf( "%s: %s is set to %s\r\n", source.c_str(), setting.c_str(), value.c_str() ); + break; + } + } + } +} + +// Write the specified setting to the specified ConfigSource +void Configurator::config_set_command( string parameters, StreamOutput* stream ){ + string source = shift_parameter(parameters); + string setting = shift_parameter(parameters); + string value = shift_parameter(parameters); + if (value == "") { + value = setting; + setting = source; + source = ""; + this->kernel->config->set_string(setting, value); + stream->printf( "live: %s has been set to %s\r\n", setting.c_str(), value.c_str() ); + } else { + uint16_t source_checksum = get_checksum(source); + for(int i=0; i < this->kernel->config->config_sources.size(); i++){ + if( this->kernel->config->config_sources[i]->is_named(source_checksum) ){ + this->kernel->config->config_sources[i]->write(setting, value); + stream->printf( "%s: %s has been set to %s\r\n", source.c_str(), setting.c_str(), value.c_str() ); + break; + } + } + } +} + +// Reload config values from the specified ConfigSource +void Configurator::config_load_command( string parameters, StreamOutput* stream ){ + string source = shift_parameter(parameters); + if(source == ""){ + this->kernel->config->config_cache_load(); + this->kernel->call_event(ON_CONFIG_RELOAD); + stream->printf( "Reloaded settings\r\n" ); + } else if(file_exists(source)){ + FileConfigSource fcs(source); + fcs.transfer_values_to_cache(&this->kernel->config->config_cache); + this->kernel->call_event(ON_CONFIG_RELOAD); + stream->printf( "Loaded settings from %s\r\n", source.c_str() ); + } else { + uint16_t source_checksum = get_checksum(source); + for(int i=0; i < this->kernel->config->config_sources.size(); i++){ + if( this->kernel->config->config_sources[i]->is_named(source_checksum) ){ + this->kernel->config->config_sources[i]->transfer_values_to_cache(&this->kernel->config->config_cache); + this->kernel->call_event(ON_CONFIG_RELOAD); + stream->printf( "Loaded settings from %s\r\n", source.c_str() ); + break; + } + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/configurator/Configurator.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,42 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#ifndef configurator_h +#define configurator_h + +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/StreamOutput.h" + + +#define CONF_NONE 0 +#define CONF_ROM 1 +#define CONF_SD 2 +#define CONF_EEPROM 3 + +#define config_get_command_checksum 46310 // "config-get" +#define config_set_command_checksum 55538 // "config-set" +#define config_load_command_checksum 3143 // "config-load" + +class Configurator : public Module { + public: + Configurator(){} + + virtual void on_module_loaded(); + virtual void on_console_line_received( void* argument ); + virtual void on_gcode_execute( void* argument ); + virtual void on_main_loop( void* argument ); + + void config_get_command( string parameters, StreamOutput* stream ); + void config_set_command( string parameters, StreamOutput* stream ); + void config_load_command(string parameters, StreamOutput* stream ); +}; + + +#endif // configurator_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/currentcontrol/CurrentControl.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,26 @@ +#include "libs/Kernel.h" +#include "CurrentControl.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include <string> +using namespace std; + +CurrentControl::CurrentControl(){} + +void CurrentControl::on_module_loaded(){ + if( !this->kernel->config->value( currentcontrol_module_enable_checksum )->by_default(false)->as_bool() ){ return; } + + // Get configuration + this->alpha_current = this->kernel->config->value(alpha_current_checksum )->by_default(0.8)->as_number(); + this->beta_current = this->kernel->config->value(beta_current_checksum )->by_default(0.8)->as_number(); + this->gamma_current = this->kernel->config->value(gamma_current_checksum )->by_default(0.8)->as_number(); + this->delta_current = this->kernel->config->value(delta_current_checksum )->by_default(0.8)->as_number(); + + this->kernel->digipot->set_current(0, this->alpha_current); + this->kernel->digipot->set_current(1, this->beta_current ); + this->kernel->digipot->set_current(2, this->gamma_current); + this->kernel->digipot->set_current(3, this->delta_current); + +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/currentcontrol/CurrentControl.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,31 @@ +#ifndef CURRENTCONTROL_H +#define CURRENTCONTROL_H + +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/Pin.h" + +#define alpha_current_checksum 22381 +#define beta_current_checksum 60163 +#define gamma_current_checksum 12906 +#define delta_current_checksum 30321 +#define currentcontrol_module_enable_checksum 38842 + +class CurrentControl : public Module { + public: + CurrentControl(); + + virtual void on_module_loaded(); + + double alpha_current; + double beta_current; + double gamma_current; + double delta_current; +}; + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/pausebutton/PauseButton.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,48 @@ +#include "libs/Kernel.h" +#include "PauseButton.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include <string> +using namespace std; + +PauseButton::PauseButton(){} + +void PauseButton::on_module_loaded(){ + this->button_state = true; + this->play_state = true; + this->register_for_event(ON_PLAY); + this->register_for_event(ON_PAUSE); + + this->button = this->kernel->config->value( pause_button_pin_checksum )->by_default("2.12")->as_pin()->as_input(); + this->led = this->kernel->config->value( pause_led_pin_checksum )->by_default("4.28")->as_pin()->as_output(); + + this->kernel->slow_ticker->attach( 100, this, &PauseButton::button_tick ); +} + +//TODO:Â Make this use InterruptIn +//Check the state of the button and act accordingly +uint32_t PauseButton::button_tick(uint32_t dummy){ + // If button changed + if(this->button_state != this->button->get()){ + this->button_state = this->button->get(); + // If button pressed + if( this->button_state ){ + if( this->play_state ){ + this->play_state = false; + this->kernel->pauser->take(); + }else{ + this->play_state = true; + this->kernel->pauser->release(); + } + } + } +} + +void PauseButton::on_play( void* argument ){ + this->led->set(0); +} + +void PauseButton::on_pause( void* argument ){ + this->led->set(1); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/pausebutton/PauseButton.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,37 @@ +#ifndef PAUSEBUTTON_H +#define PAUSEBUTTON_H + +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/Pin.h" + +#define pause_button_pin_checksum 32709 +#define pause_led_pin_checksum 48477 + +class PauseButton : public Module { + public: + PauseButton(); + + virtual void on_module_loaded(); + uint32_t button_tick(uint32_t dummy); + virtual void on_play( void* argument ); + virtual void on_pause( void* argument ); + + Pin* button; + Pin* led; + bool button_state; + bool play_state; +}; + + + + + + + + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/simpleshell/SimpleShell.cpp Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,162 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "libs/Kernel.h" +#include "SimpleShell.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/SerialMessage.h" +#include "libs/StreamOutput.h" +#include "modules/robot/Player.h" + + +void SimpleShell::on_module_loaded(){ + this->current_path = "/"; + this->playing_file = false; + this->register_for_event(ON_CONSOLE_LINE_RECEIVED); + this->register_for_event(ON_MAIN_LOOP); +} + +// When a new line is received, check if it is a command, and if it is, act upon it +void SimpleShell::on_console_line_received( void* argument ){ + SerialMessage new_message = *static_cast<SerialMessage*>(argument); + string possible_command = new_message.message; + + // We don't compare to a string but to a checksum of that string, this saves some space in flash memory + unsigned short check_sum = get_checksum( possible_command.substr(0,possible_command.find_first_of(" \r\n")) ); // todo:Â put this method somewhere more convenient + + // Act depending on command + switch( check_sum ){ + case ls_command_checksum : this->ls_command( get_arguments(possible_command), new_message.stream ); break; + case cd_command_checksum : this->cd_command( get_arguments(possible_command), new_message.stream ); break; + case pwd_command_checksum : this->pwd_command( get_arguments(possible_command), new_message.stream ); break; + case cat_command_checksum : this->cat_command( get_arguments(possible_command), new_message.stream ); break; + case play_command_checksum : this->play_command(get_arguments(possible_command), new_message.stream ); break; + case reset_command_checksum : this->reset_command(get_arguments(possible_command),new_message.stream ); break; + } +} + +// Convert a path indication ( absolute or relative ) into a path ( absolute ) +string SimpleShell::absolute_from_relative( string path ){ + if( path[0] == '/' ){ return path; } + if( path[0] == '.' ){ return this->current_path; } + return this->current_path + path; +} + +// Act upon an ls command +// Convert the first parameter into an absolute path, then list the files in that path +void SimpleShell::ls_command( string parameters, StreamOutput* stream ){ + string folder = this->absolute_from_relative( parameters ); + DIR* d; + struct dirent* p; + d = opendir(folder.c_str()); + if(d != NULL) { + while((p = readdir(d)) != NULL) { stream->printf("%s\r\n", lc(string(p->d_name)).c_str()); } + } else { + stream->printf("Could not open directory %s \r\n", folder.c_str()); + } +} + +// Change current absolute path to provided path +void SimpleShell::cd_command( string parameters, StreamOutput* stream ){ + string folder = this->absolute_from_relative( parameters ); + if( folder[folder.length()-1] != '/' ){ folder += "/"; } + DIR *d; + struct dirent *p; + d = opendir(folder.c_str()); + if(d == NULL) { + stream->printf("Could not open directory %s \r\n", folder.c_str() ); + }else{ + this->current_path = folder; + } +} + +// Responds with the present working directory +void SimpleShell::pwd_command( string parameters, StreamOutput* stream ){ + stream->printf("%s\r\n", this->current_path.c_str()); +} + +// Output the contents of a file, first parameter is the filename, second is the limit ( in number of lines to output ) +void SimpleShell::cat_command( string parameters, StreamOutput* stream ){ + + // Get parameters ( filename and line limit ) + string filename = this->absolute_from_relative(shift_parameter( parameters )); + string limit_paramater = shift_parameter( parameters ); + int limit = -1; + if( limit_paramater != "" ){ limit = int(atof(limit_paramater.c_str())); } + + // Open file + FILE *lp = fopen(filename.c_str(), "r"); + if(lp == NULL) { + stream->printf("File not found: %s\r\n", filename.c_str()); + return; + } + string buffer; + int c; + int newlines = 0; + + // Print each line of the file + while ((c = fgetc (lp)) != EOF){ + buffer.append((char *)&c, 1); + if( char(c) == '\n' ){ + newlines++; + stream->printf("%s", buffer.c_str()); + buffer.clear(); + } + if( newlines == limit ){ break; } + }; + fclose(lp); + +} + +// Play a gcode file by considering each line as if it was received on the serial console +void SimpleShell::play_command( string parameters, StreamOutput* stream ){ + // Get filename + string filename = this->absolute_from_relative(shift_parameter( parameters )); + this->current_file_handler = fopen( filename.c_str(), "r"); + if(this->current_file_handler == NULL) + { + stream->printf("File not found: %s\r\n", filename.c_str()); + return; + } + this->playing_file = true; + this->current_stream = stream; +} + +// Reset the system +void SimpleShell::reset_command( string parameters, StreamOutput* stream){ + stream->printf("Smoothie out. Peace.\r\n"); + system_reset(); +} + +void SimpleShell::on_main_loop(void* argument){ + + if( this->playing_file ){ + string buffer; + int c; + // Print each line of the file + while ((c = fgetc(this->current_file_handler)) != EOF){ + if (c == '\n'){ + this->current_stream->printf("%s\n", buffer.c_str()); + struct SerialMessage message; + message.message = buffer; + message.stream = this->current_stream; + // wait for the queue to have enough room that a serial message could still be received before sending + this->kernel->player->wait_for_queue(2); + this->kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message); + buffer.clear(); + return; + }else{ + buffer += c; + } + }; + + fclose(this->current_file_handler); + this->playing_file = false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/utils/simpleshell/SimpleShell.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,47 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#ifndef simpleshell_h +#define simpleshell_h + +#include "libs/Kernel.h" +#include "libs/nuts_bolts.h" +#include "libs/utils.h" +#include "libs/StreamOutput.h" + + +#define ls_command_checksum 19679 +#define cd_command_checksum 11207 +#define pwd_command_checksum 42060 +#define cat_command_checksum 24889 +#define play_command_checksum 17335 +#define reset_command_checksum 27429 + +class SimpleShell : public Module { + public: + SimpleShell(){} + + virtual void on_module_loaded(); + virtual void on_console_line_received( void* argument ); + virtual void on_main_loop( void* argument ); + string absolute_from_relative( string path ); + void ls_command( string parameters, StreamOutput* stream ); + void cd_command( string parameters, StreamOutput* stream ); + void pwd_command( string parameters, StreamOutput* stream ); + void cat_command( string parameters, StreamOutput* stream ); + void play_command( string parameters, StreamOutput* stream ); + void reset_command(string parameters, StreamOutput* stream ); + + string current_path; + bool playing_file; + StreamOutput* current_stream; + FILE* current_file_handler; +}; + + +#endif