A complete refactor of the old spindle control module.
authorbouni <bouni@owee.de>
Fri, 8 May 2015 20:05:20 +0000 (22:05 +0200)
committerbouni <bouni@owee.de>
Fri, 8 May 2015 20:05:20 +0000 (22:05 +0200)
The following things have been done so far:
- Added a SoftSerial lib (found on mbed.org)
- Added a Modbus lib (not all functions implemented yet)
- Moved the former spindle module code into PWMSpindleControl
- Added a AnalogSpindleControl for 0-10V control of VFDs
- Added a ModbusSpindleControl for contrlling spindles over RS485
- Added a SpindleMaker that loads the correct Spindle Module depending
  on the config

24 files changed:
src/libs/Modbus/Modbus.cpp [new file with mode: 0644]
src/libs/Modbus/Modbus.h [new file with mode: 0644]
src/libs/SoftSerial/Buffer.cpp [new file with mode: 0644]
src/libs/SoftSerial/Buffer.h [new file with mode: 0644]
src/libs/SoftSerial/BufferedSoftSerial.cpp [new file with mode: 0644]
src/libs/SoftSerial/BufferedSoftSerial.h [new file with mode: 0644]
src/libs/SoftSerial/SoftSerial.cpp [new file with mode: 0644]
src/libs/SoftSerial/SoftSerial.h [new file with mode: 0644]
src/libs/SoftSerial/SoftSerial_Ticker.h [new file with mode: 0644]
src/libs/SoftSerial/SoftSerial_rx.cpp [new file with mode: 0644]
src/libs/SoftSerial/SoftSerial_tx.cpp [new file with mode: 0644]
src/main.cpp
src/modules/tools/spindle/AnalogSpindleControl.cpp [new file with mode: 0644]
src/modules/tools/spindle/AnalogSpindleControl.h [new file with mode: 0644]
src/modules/tools/spindle/HuanyangSpindleControl.cpp [new file with mode: 0644]
src/modules/tools/spindle/HuanyangSpindleControl.h [new file with mode: 0644]
src/modules/tools/spindle/ModbusSpindleControl.cpp [new file with mode: 0644]
src/modules/tools/spindle/ModbusSpindleControl.h [new file with mode: 0644]
src/modules/tools/spindle/PWMSpindleControl.cpp [moved from src/modules/tools/spindle/Spindle.cpp with 50% similarity]
src/modules/tools/spindle/PWMSpindleControl.h [moved from src/modules/tools/spindle/Spindle.h with 75% similarity]
src/modules/tools/spindle/SpindleControl.cpp [new file with mode: 0644]
src/modules/tools/spindle/SpindleControl.h [new file with mode: 0644]
src/modules/tools/spindle/SpindleMaker.cpp [new file with mode: 0644]
src/modules/tools/spindle/SpindleMaker.h [new file with mode: 0644]

diff --git a/src/libs/Modbus/Modbus.cpp b/src/libs/Modbus/Modbus.cpp
new file mode 100644 (file)
index 0000000..cdd3538
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+      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 "libs/gpio.h"
+#include "SoftSerial.h"
+#include "Modbus.h"
+
+Modbus::Modbus( PinName tx_pin, PinName rx_pin, PinName dir_pin){
+    serial = new BufferedSoftSerial( tx_pin, rx_pin );
+    serial->baud(9600);
+    serial->format(8,serial->Parity::None,1);
+    calculate_delay(9600, 8, 0, 1);
+    dir_output = new GPIO(dir_pin);
+    dir_output->output();
+    dir_output->clear();
+}
+
+Modbus::Modbus( PinName tx_pin, PinName rx_pin, PinName dir_pin, int baud_rate){
+    serial = new BufferedSoftSerial( tx_pin, rx_pin );
+    serial->baud(baud_rate);
+    serial->format(8,serial->Parity::None,1);
+    calculate_delay(baud_rate, 8, 0, 1);
+    dir_output = new GPIO(dir_pin);
+    dir_output->output();
+    dir_output->clear();
+}
+
+Modbus::Modbus( PinName tx_pin, PinName rx_pin, PinName dir_pin, int baud_rate, const char *format){
+    serial = new BufferedSoftSerial( tx_pin, rx_pin );
+    serial->baud(baud_rate);
+    
+    if(strncmp(format, "8N1", 3) == 0){
+        serial->format(8,serial->Parity::None,1);
+        calculate_delay(baud_rate, 8, 0, 1);
+    } else if(strncmp(format, "8O1", 3) == 0){
+        serial->format(8,serial->Parity::Odd,1);
+        calculate_delay(baud_rate, 8, 1, 1);
+    } else if(strncmp(format, "8E1", 3) == 0){
+        serial->format(8,serial->Parity::Even,1);
+        calculate_delay(baud_rate, 8, 1, 1);
+    } else if(strncmp(format, "8N2", 3) == 0){
+        serial->format(8,serial->Parity::None,2);
+        calculate_delay(baud_rate, 8, 0, 2);
+    } else {
+        serial->format(8,serial->Parity::None,1);
+        calculate_delay(baud_rate, 8, 0, 1);
+    }
+    dir_output = new GPIO(dir_pin);
+    dir_output->output();
+    dir_output->clear();
+}
+
+// Called when the module has just been loaded
+void Modbus::on_module_loaded() {
+    // We want to be called every time a new char is received
+    serial->attach(this, &Modbus::on_serial_char_received, BufferedSoftSerial::RxIrq);
+}
+
+// Called on Serial::RxIrq interrupt, meaning we have received a char
+void Modbus::on_serial_char_received(){
+    buffer.push_back(serial->getc());
+}
+
+void Modbus::read_coil(int slave_addr, int coil_addr, int n_coils){
+    // TODO: implement this
+}
+
+void Modbus::read_holding_register(int slave_addr, int reg_addr, int n_regs){
+    // TODO: implement this
+}
+
+void Modbus::write_coil(int slave_addr, int coil_addr, bool data){
+    char telegram[8];
+    unsigned int crc;
+    telegram[0] = slave_addr;       // Slave address
+    telegram[1] = 0x05;             // Function code
+    telegram[2] = (coil_addr >> 8); // Coil address MSB
+    telegram[3] = coil_addr;        // Coil address LSB
+    telegram[4] = 0x00;             // Data MSB
+    telegram[5] = (data == true) ? 0xFF : 0x00; // Data LSB
+    crc = crc16(telegram, 6);       
+    telegram[6] = crc;              // CRC LSB
+    telegram[7] = (crc >> 8);       // CRC MSB
+    dir_output->set();
+    serial->write(telegram,8);
+    delay((int) ceil(50 + 8 * delay_time));
+    dir_output->clear();
+}
+
+
+void Modbus::write_holding_register(int slave_addr, int reg_addr, int data){
+    char telegram[8];
+    unsigned int crc;
+    telegram[0] = slave_addr;       // Slave address
+    telegram[1] = 0x06;             // Function code
+    telegram[2] = (reg_addr >> 8);  // Register address MSB
+    telegram[3] = reg_addr;         // Register address LSB
+    telegram[4] = (data >> 8);      // Data MSB
+    telegram[5] = data;             // Data LSB
+    crc = crc16(telegram, 6);       
+    telegram[6] = crc;              // CRC LSB
+    telegram[7] = (crc >> 8);       // CRC MSB
+    dir_output->set();
+    serial->write(telegram,8);
+    delay((int) ceil(50 + 8 * delay_time));
+    dir_output->clear();
+}
+
+void Modbus::diagnostic(int slave_addr, int test_sub_code, int data){
+    // TODO: implement this
+}
+
+void Modbus::write_multiple_coils(int slave_addr, int coil_addr, int n_coils, int data){
+    // TODO: implement this
+}
+
+void Modbus::write_multiple_registers(int slave_addr, int start_addr, int data){
+    // TODO: implement this
+}
+
+void Modbus::read_write_multiple_holding_registers(int slave_addr, int read_addr, int n_read, int write_addr, int data){
+    // TODO: implement this
+}
+
+void Modbus::calculate_delay(int baudrate, int bits, int parity, int stop) {
+
+    float bittime = 1000.0 / baudrate;
+    // here we calculate how long a byte with all surrounding bits take
+    // startbit + number of bits + parity bit + stop bit
+    delay_time = bittime * (1 + bits + parity + 1);
+}
+
+void Modbus::delay(unsigned int value) {
+    
+    uint32_t start = us_ticker_read(); // mbed call
+    while ((us_ticker_read() - start) < value*1000) {
+        THEKERNEL->call_event(ON_IDLE, this);
+    }
+
+}
+
+unsigned int Modbus::crc16(char *data, unsigned int len) {
+    
+    static const unsigned short crc_table[] = {
+    0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
+    0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
+    0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
+    0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
+    0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
+    0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
+    0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
+    0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
+    0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
+    0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
+    0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
+    0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
+    0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
+    0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
+    0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
+    0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
+    0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
+    0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
+    0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
+    0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
+    0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
+    0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
+    0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
+    0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
+    0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
+    0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
+    0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
+    0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
+    0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
+    0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
+    0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
+    0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040
+    };
+
+    unsigned char tmp;
+    unsigned short crc = 0xFFFF;
+
+    while(len--) {
+        tmp = *data++ ^ crc;
+        crc = crc >> 8;
+        crc ^= crc_table[tmp];
+    }
+
+    return crc;
+
+}
diff --git a/src/libs/Modbus/Modbus.h b/src/libs/Modbus/Modbus.h
new file mode 100644 (file)
index 0000000..25021a6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    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 MODBUS_H
+#define MODBUS_H
+
+#include "libs/Module.h"
+#include "BufferedSoftSerial.h"
+#include "libs/Kernel.h"
+#include <vector>
+#include <string>
+using std::string;
+
+class GPIO;
+
+class Modbus : public Module {
+    public:
+        Modbus( PinName rx_pin, PinName tx_pin, PinName dir_pin);
+        Modbus( PinName rx_pin, PinName tx_pin, PinName dir_pin, int baud_rate);
+        Modbus( PinName rx_pin, PinName tx_pin, PinName dir_pin, int baud_rate, const char *format);
+
+        void on_module_loaded();
+        void on_serial_char_received();
+
+        void read_coil(int slave_addr, int coil_addr, int n_coils);
+        void read_holding_register(int slave_addr, int reg_addr, int n_regs);
+        void write_coil(int slave_addr, int coil_addr, bool data);
+        void write_holding_register(int slave_addr, int reg_addr, int data);
+        void diagnostic(int slave_addr, int test_sub_code, int data);
+        void write_multiple_coils(int slave_addr, int coil_addr, int n_coils, int data);
+        void write_multiple_registers(int slave_addr, int start_addr, int data);
+        unsigned int crc16(char *data, unsigned int len); 
+        void read_write_multiple_holding_registers(int slave_addr, int read_addr, int n_read, int write_addr, int data);
+        void calculate_delay(int baudrate, int bits, int parity, int stop);
+        void delay(unsigned int);
+
+        GPIO *dir_output;
+
+        BufferedSoftSerial* serial;
+        std::vector<int> buffer;
+        static int active;
+
+        float delay_time;        
+};
+
+#endif
diff --git a/src/libs/SoftSerial/Buffer.cpp b/src/libs/SoftSerial/Buffer.cpp
new file mode 100644 (file)
index 0000000..03a6067
--- /dev/null
@@ -0,0 +1,76 @@
+\r
+/**\r
+ * @file    Buffer.cpp\r
+ * @brief   Software Buffer - Templated Ring Buffer for most data types\r
+ * @author  sam grove\r
+ * @version 1.0\r
+ * @see     \r
+ *\r
+ * Copyright (c) 2013\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
\r
+#include "Buffer.h"\r
+\r
+template <class T>\r
+Buffer<T>::Buffer(uint32_t size)\r
+{\r
+    _buf = new T [size];\r
+    _size = size;\r
+    clear();\r
+    \r
+    return;\r
+}\r
+\r
+template <class T>\r
+Buffer<T>::~Buffer()\r
+{\r
+    delete [] _buf;\r
+    \r
+    return;\r
+}\r
+\r
+template <class T>\r
+uint32_t Buffer<T>::getSize() \r
+{ \r
+    return _size; \r
+}\r
+\r
+template <class T>\r
+void Buffer<T>::clear(void)\r
+{\r
+    _wloc = 0;\r
+    _rloc = 0;\r
+    memset(_buf, 0, _size);\r
+    \r
+    return;\r
+}\r
+\r
+template <class T>\r
+uint32_t Buffer<T>::peek(char c)\r
+{\r
+    return 1;\r
+}\r
+\r
+// make the linker aware of some possible types\r
+template class Buffer<uint8_t>;\r
+template class Buffer<int8_t>;\r
+template class Buffer<uint16_t>;\r
+template class Buffer<int16_t>;\r
+template class Buffer<uint32_t>;\r
+template class Buffer<int32_t>;\r
+template class Buffer<uint64_t>;\r
+template class Buffer<int64_t>;\r
+template class Buffer<char>;\r
+template class Buffer<wchar_t>;\r
diff --git a/src/libs/SoftSerial/Buffer.h b/src/libs/SoftSerial/Buffer.h
new file mode 100644 (file)
index 0000000..4350b98
--- /dev/null
@@ -0,0 +1,163 @@
+\r
+/**\r
+ * @file    Buffer.h\r
+ * @brief   Software Buffer - Templated Ring Buffer for most data types\r
+ * @author  sam grove\r
+ * @version 1.0\r
+ * @see     \r
+ *\r
+ * Copyright (c) 2013\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
\r
+#ifndef BUFFER_H\r
+#define BUFFER_H\r
+\r
+#include <stdint.h>\r
+#include <string.h>\r
+\r
+/** A templated software ring buffer\r
+ *\r
+ * Example:\r
+ * @code\r
+ *  #include "mbed.h"\r
+ *  #include "Buffer.h"\r
+ *\r
+ *  Buffer <char> buf;\r
+ *\r
+ *  int main()\r
+ *  {\r
+ *      buf = 'a';\r
+ *      buf.put('b');\r
+ *      char *head = buf.head();\r
+ *      puts(head);\r
+ *\r
+ *      char whats_in_there[2] = {0};\r
+ *      int pos = 0;\r
+ *\r
+ *      while(buf.available())\r
+ *      {   \r
+ *          whats_in_there[pos++] = buf;\r
+ *      }\r
+ *      printf("%c %c\n", whats_in_there[0], whats_in_there[1]);\r
+ *      buf.clear();\r
+ *      error("done\n\n\n");\r
+ *  }\r
+ * @endcode\r
+ */\r
+\r
+template <typename T>\r
+class Buffer\r
+{\r
+private:\r
+    T   *_buf;\r
+    volatile uint32_t   _wloc;\r
+    volatile uint32_t   _rloc;\r
+    uint32_t            _size;\r
+\r
+public:\r
+    /** Create a Buffer and allocate memory for it\r
+     *  @param size The size of the buffer\r
+     */\r
+    Buffer(uint32_t size = 0x100);\r
+    \r
+    /** Get the size of the ring buffer\r
+     * @return the size of the ring buffer\r
+     */\r
+     uint32_t getSize();\r
+    \r
+    /** Destry a Buffer and release it's allocated memory\r
+     */\r
+    ~Buffer();\r
+    \r
+    /** Add a data element into the buffer\r
+     *  @param data Something to add to the buffer\r
+     */\r
+    void put(T data);\r
+    \r
+    /** Remove a data element from the buffer\r
+     *  @return Pull the oldest element from the buffer\r
+     */\r
+    T get(void);\r
+    \r
+    /** Get the address to the head of the buffer\r
+     *  @return The address of element 0 in the buffer\r
+     */\r
+    T *head(void);\r
+    \r
+    /** Reset the buffer to 0. Useful if using head() to parse packeted data\r
+     */\r
+    void clear(void);\r
+    \r
+    /** Determine if anything is readable in the buffer\r
+     *  @return 1 if something can be read, 0 otherwise\r
+     */\r
+    uint32_t available(void);\r
+    \r
+    /** Overloaded operator for writing to the buffer\r
+     *  @param data Something to put in the buffer\r
+     *  @return\r
+     */\r
+    Buffer &operator= (T data)\r
+    {\r
+        put(data);\r
+        return *this;\r
+    }\r
+    \r
+    /** Overloaded operator for reading from the buffer\r
+     *  @return Pull the oldest element from the buffer \r
+     */  \r
+    operator int(void)\r
+    {\r
+        return get();\r
+    }\r
+    \r
+     uint32_t peek(char c);\r
+    \r
+};\r
+\r
+template <class T>\r
+inline void Buffer<T>::put(T data)\r
+{\r
+    _buf[_wloc++] = data;\r
+    _wloc %= (_size-1);\r
+    \r
+    return;\r
+}\r
+\r
+template <class T>\r
+inline T Buffer<T>::get(void)\r
+{\r
+    T data_pos = _buf[_rloc++];\r
+    _rloc %= (_size-1);\r
+    \r
+    return data_pos;\r
+}\r
+\r
+template <class T>\r
+inline T *Buffer<T>::head(void)\r
+{\r
+    T *data_pos = &_buf[0];\r
+    \r
+    return data_pos;\r
+}\r
+\r
+template <class T>\r
+inline uint32_t Buffer<T>::available(void)\r
+{\r
+    return (_wloc == _rloc) ? 0 : 1;\r
+}\r
+\r
+#endif\r
+\r
diff --git a/src/libs/SoftSerial/BufferedSoftSerial.cpp b/src/libs/SoftSerial/BufferedSoftSerial.cpp
new file mode 100644 (file)
index 0000000..68ad3f4
--- /dev/null
@@ -0,0 +1,140 @@
+/**\r
+ * @file    BufferedSoftSerial.cpp\r
+ * @brief   Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX\r
+ * @author  sam grove\r
+ * @version 1.0\r
+ * @see\r
+ *\r
+ * Copyright (c) 2013\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+#include "BufferedSoftSerial.h"\r
+#include <stdarg.h>\r
+\r
+BufferedSoftSerial::BufferedSoftSerial(PinName tx, PinName rx, const char* name)\r
+    : SoftSerial(tx, rx, name)\r
+{\r
+    SoftSerial::attach(this, &BufferedSoftSerial::rxIrq, SoftSerial::RxIrq);\r
+\r
+    return;\r
+}\r
+\r
+int BufferedSoftSerial::readable(void)\r
+{\r
+    return _rxbuf.available();  // note: look if things are in the buffer\r
+}\r
+\r
+int BufferedSoftSerial::writeable(void)\r
+{\r
+    return 1;   // buffer allows overwriting by design, always true\r
+}\r
+\r
+int BufferedSoftSerial::getc(void)\r
+{\r
+    return _rxbuf;\r
+}\r
+\r
+int BufferedSoftSerial::putc(int c)\r
+{\r
+    _txbuf = (char)c;\r
+    BufferedSoftSerial::prime();\r
+\r
+    return c;\r
+}\r
+\r
+int BufferedSoftSerial::puts(const char *s)\r
+{\r
+    const char* ptr = s;\r
+\r
+    while(*(ptr) != 0) {\r
+        _txbuf = *(ptr++);\r
+    }\r
+    _txbuf = '\n';  // done per puts definition\r
+    BufferedSoftSerial::prime();\r
+\r
+    return (ptr - s) + 1;\r
+}\r
+\r
+int BufferedSoftSerial::printf(const char* format, ...)\r
+{\r
+    char buf[256] = {0};\r
+    int r = 0;\r
+\r
+    va_list arg;\r
+    va_start(arg, format);\r
+    r = vsprintf(buf, format, arg);\r
+    // this may not hit the heap but should alert the user anyways\r
+    if(r > sizeof(buf)) {\r
+        error("%s %d buffer overwrite!\n", __FILE__, __LINE__);\r
+    }\r
+    va_end(arg);\r
+    r = BufferedSoftSerial::write(buf, r);\r
+\r
+    return r;\r
+}\r
+\r
+ssize_t BufferedSoftSerial::write(const void *s, size_t length)\r
+{\r
+    const char* ptr = (const char*)s;\r
+    const char* end = ptr + length;\r
+\r
+    while (ptr != end) {\r
+        _txbuf = *(ptr++);\r
+    }\r
+    BufferedSoftSerial::prime();\r
+\r
+    return ptr - (const char*)s;\r
+}\r
+\r
+\r
+void BufferedSoftSerial::rxIrq(void)\r
+{\r
+    // read from the peripheral and make sure something is available\r
+    if(SoftSerial::readable()) {\r
+        _rxbuf = _getc(); // if so load them into a buffer\r
+    }\r
+\r
+    return;\r
+}\r
+\r
+void BufferedSoftSerial::txIrq(void)\r
+{\r
+    // see if there is room in the hardware fifo and if something is in the software fifo\r
+    while(SoftSerial::writeable()) {\r
+        if(_txbuf.available()) {\r
+            _putc((int)_txbuf.get());\r
+        } else {\r
+            // disable the TX interrupt when there is nothing left to send\r
+            SoftSerial::attach(NULL, SoftSerial::TxIrq);\r
+            break;\r
+        }\r
+    }\r
+\r
+    return;\r
+}\r
+\r
+void BufferedSoftSerial::prime(void)\r
+{\r
+    // if already busy then the irq will pick this up\r
+    if(SoftSerial::writeable()) {\r
+        SoftSerial::attach(NULL, SoftSerial::TxIrq);    // make sure not to cause contention in the irq\r
+        BufferedSoftSerial::txIrq();                // only write to hardware in one place\r
+        SoftSerial::attach(this, &BufferedSoftSerial::txIrq, SoftSerial::TxIrq);\r
+    }\r
+\r
+    return;\r
+}\r
+\r
+\r
diff --git a/src/libs/SoftSerial/BufferedSoftSerial.h b/src/libs/SoftSerial/BufferedSoftSerial.h
new file mode 100644 (file)
index 0000000..2436d46
--- /dev/null
@@ -0,0 +1,139 @@
+\r
+/**\r
+ * @file    BufferedSoftSerial.h\r
+ * @brief   Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX\r
+ * @author  sam grove\r
+ * @version 1.0\r
+ * @see     \r
+ *\r
+ * Copyright (c) 2013\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+#ifndef BUFFEREDSOFTSERIAL_H\r
+#define BUFFEREDSOFTSERIAL_H\r
\r
+#include "mbed.h"\r
+#include "Buffer.h"\r
+#include "SoftSerial.h"\r
+\r
+/** A serial port (UART) for communication with other serial devices\r
+ *\r
+ * Can be used for Full Duplex communication, or Simplex by specifying\r
+ * one pin as NC (Not Connected)\r
+ *\r
+ * This uses software serial emulation, regular serial pins are alot better,\r
+ * however if you don't have spare ones, you can use this. It is advicable\r
+ * to put the serial connection with highest speed to hardware serial.\r
+ *\r
+ * If you lack RAM memory you can also use SoftSerial without this buffer around it.\r
+ * In that case it is fully blocking.\r
+ *\r
+ * Example:\r
+ * @code\r
+ * #include "mbed.h"\r
+ * #include "BufferedSoftSerial.h"\r
+ * \r
+ * SoftSerial block(USBTX, USBRX);\r
+* BufferedSoftSerial buf(USBTX, USBRX);\r
+ * \r
+ * int main()\r
+ * {\r
+ *     while(1) {\r
+ *         Timer s;\r
+ * \r
+ *         s.start();\r
+ *         buf.printf("Hello World - buffered\r\n");\r
+ *         int buffered_time = s.read_us();\r
+ *         wait(0.1f); // give time for the buffer to empty\r
+ * \r
+ *         s.reset();\r
+ *         block.printf("Hello World - blocking\r\n");\r
+ *         int polled_time = s.read_us();\r
+ *         s.stop();\r
+ *         wait(0.1f); // give time for the buffer to empty\r
+ * \r
+ *         buf.printf("printf buffered took %d us\r\n", buffered_time);\r
+ *         buf.printf("printf blocking took %d us\r\n", polled_time);\r
+ *         wait(5);\r
+ *     }\r
+ * }\r
+ * @endcode\r
+ */\r
+\r
+/**\r
+ *  @class BufferedSerial\r
+ *  @brief Software buffers and interrupt driven tx and rx for SoftSerial\r
+ */  \r
+class BufferedSoftSerial : public SoftSerial \r
+{\r
+private:\r
+    Buffer <char> _rxbuf;\r
+    Buffer <char> _txbuf;\r
\r
+    void rxIrq(void);\r
+    void txIrq(void);\r
+    void prime(void);\r
+    \r
+public:\r
+    /** Create a BufferedSoftSerial port, connected to the specified transmit and receive pins\r
+     *  @param tx Transmit pin\r
+     *  @param rx Receive pin\r
+     *  @note Either tx or rx may be specified as NC if unused\r
+     */\r
+    BufferedSoftSerial(PinName tx, PinName rx, const char* name=NULL);\r
+    \r
+    /** Check on how many bytes are in the rx buffer\r
+     *  @return 1 if something exists, 0 otherwise\r
+     */\r
+    virtual int readable(void);\r
+    \r
+    /** Check to see if the tx buffer has room\r
+     *  @return 1 always has room and can overwrite previous content if too small / slow\r
+     */\r
+    virtual int writeable(void);\r
+    \r
+    /** Get a single byte from the BufferedSoftSerial Port.\r
+     *  Should check readable() before calling this.\r
+     *  @return A byte that came in on the BufferedSoftSerial Port\r
+     */\r
+    virtual int getc(void);\r
+    \r
+    /** Write a single byte to the BufferedSoftSerial Port.\r
+     *  @param c The byte to write to the BufferedSoftSerial Port\r
+     *  @return The byte that was written to the BufferedSoftSerial Port Buffer\r
+     */\r
+    virtual int putc(int c);\r
+    \r
+    /** Write a string to the BufferedSoftSerial Port. Must be NULL terminated\r
+     *  @param s The string to write to the Serial Port\r
+     *  @return The number of bytes written to the Serial Port Buffer\r
+     */\r
+    virtual int puts(const char *s);\r
+    \r
+    /** Write a formatted string to the BufferedSoftSerial Port.\r
+     *  @param format The string + format specifiers to write to the BufferedSoftSerial Port\r
+     *  @return The number of bytes written to the Serial Port Buffer\r
+     */\r
+    virtual int printf(const char* format, ...);\r
+    \r
+    /** Write data to the BufferedSoftSerial Port\r
+     *  @param s A pointer to data to send\r
+     *  @param length The amount of data being pointed to\r
+     *  @return The number of bytes written to the Serial Port Buffer\r
+     */\r
+    virtual ssize_t write(const void *s, std::size_t length);\r
+};\r
+\r
+#endif\r
diff --git a/src/libs/SoftSerial/SoftSerial.cpp b/src/libs/SoftSerial/SoftSerial.cpp
new file mode 100644 (file)
index 0000000..55f81cd
--- /dev/null
@@ -0,0 +1,47 @@
+/* \r
+ * SoftSerial by Erik Olieman\r
+ * Date: 05 Jul 2014\r
+ * Revision: 10:236fce2e5b8c\r
+ * URL: http://developer.mbed.org/users/Sissors/code/SoftSerial/\r
+ */\r
+\r
+#include "SoftSerial.h"\r
+\r
+SoftSerial::SoftSerial(PinName TX, PinName RX, const char* name) {\r
+    tx_en = rx_en = false;\r
+    if (TX != NC) {\r
+        tx = new DigitalOut(TX);\r
+        tx_en = true;\r
+        tx->write(1);\r
+        tx_bit = -1;\r
+        txticker.attach(this, &SoftSerial::tx_handler);\r
+    }\r
+    if (RX != NC) {\r
+        rx = new InterruptIn(RX);\r
+        rx_en = true;\r
+        out_valid = false;\r
+        rxticker.attach(this, &SoftSerial::rx_handler);\r
+        rx->fall(this, &SoftSerial::rx_gpio_irq_handler);\r
+    }\r
+    \r
+    baud(9600);\r
+    format();\r
+}\r
+\r
+SoftSerial::~SoftSerial() {\r
+    if (tx_en)\r
+        delete(tx);\r
+    if (rx_en)\r
+        delete(rx);\r
+}\r
+\r
+void SoftSerial::baud(int baudrate) {\r
+    bit_period = 1000000 / baudrate;\r
+}\r
+\r
+void SoftSerial::format(int bits, Parity parity, int stop_bits) {\r
+    _bits = bits;\r
+    _parity = parity;\r
+    _stop_bits = stop_bits;\r
+    _total_bits = 1 + _bits + _stop_bits + (bool)_parity;\r
+}\r
diff --git a/src/libs/SoftSerial/SoftSerial.h b/src/libs/SoftSerial/SoftSerial.h
new file mode 100644 (file)
index 0000000..947ba19
--- /dev/null
@@ -0,0 +1,131 @@
+/* \r
+ * SoftSerial by Erik Olieman\r
+ * Date: 05 Jul 2014\r
+ * Revision: 10:236fce2e5b8c\r
+ * URL: http://developer.mbed.org/users/Sissors/code/SoftSerial/\r
+ */\r
+\r
+#ifndef SOFTSERIAL_H\r
+#define SOFTSERIAL_H\r
+\r
+#include "mbed.h"\r
+#include "SoftSerial_Ticker.h"\r
+/** A software serial implementation\r
+ *\r
+ */\r
+class SoftSerial: public Stream {\r
+\r
+public:\r
+    /**\r
+    * Constructor\r
+    *\r
+    * @param TX Name of the TX pin, NC for not connected\r
+    * @param RX Name of the RX pin, NC for not connected, must be capable of being InterruptIn\r
+    * @param name Name of the connection\r
+    */\r
+    SoftSerial(PinName TX, PinName RX, const char* name = NULL);\r
+    virtual ~SoftSerial();\r
+    \r
+    /** Set the baud rate of the serial port\r
+     *\r
+     *  @param baudrate The baudrate of the serial port (default = 9600).\r
+     */\r
+    void baud(int baudrate);\r
+\r
+    enum Parity {\r
+        None = 0,\r
+        Odd,\r
+        Even,\r
+        Forced1,\r
+        Forced0\r
+    };\r
+\r
+    enum IrqType {\r
+        RxIrq = 0,\r
+        TxIrq\r
+    };\r
+\r
+    /** Set the transmission format used by the serial port\r
+     *\r
+     *  @param bits The number of bits in a word (default = 8)\r
+     *  @param parity The parity used (SerialBase::None, SerialBase::Odd, SerialBase::Even, SerialBase::Forced1, SerialBase::Forced0; default = SerialBase::None)\r
+     *  @param stop The number of stop bits (default = 1)\r
+     */\r
+    void format(int bits=8, Parity parity=SoftSerial::None, int stop_bits=1);\r
+\r
+    /** Determine if there is a character available to read\r
+     *\r
+     *  @returns\r
+     *    1 if there is a character available to read,\r
+     *    0 otherwise\r
+     */\r
+    int readable();\r
+\r
+    /** Determine if there is space available to write a character\r
+     *\r
+     *  @returns\r
+     *    1 if there is space to write a character,\r
+     *    0 otherwise\r
+     */\r
+    int writeable();\r
+\r
+    /** Attach a function to call whenever a serial interrupt is generated\r
+     *\r
+     *  @param fptr A pointer to a void function, or 0 to set as none\r
+     *  @param type Which serial interrupt to attach the member function to (Seriall::RxIrq for receive, TxIrq for transmit buffer empty)\r
+     */\r
+    void attach(void (*fptr)(void), IrqType type=RxIrq) {\r
+        fpointer[type].attach(fptr);\r
+    }\r
+\r
+    /** Attach a member function to call whenever a serial interrupt is generated\r
+     *\r
+     *  @param tptr pointer to the object to call the member function on\r
+     *  @param mptr pointer to the member function to be called\r
+     *  @param type Which serial interrupt to attach the member function to (Seriall::RxIrq for receive, TxIrq for transmit buffer empty)\r
+     */\r
+    template<typename T>\r
+    void attach(T* tptr, void (T::*mptr)(void), IrqType type=RxIrq) {\r
+        fpointer[type].attach(tptr, mptr);\r
+    }\r
+\r
+    /** Generate a break condition on the serial line\r
+     */\r
+    void send_break();\r
+\r
+protected:\r
+    DigitalOut *tx;\r
+    InterruptIn *rx;\r
+    \r
+    bool tx_en, rx_en;\r
+    int bit_period;\r
+    int _bits, _stop_bits, _total_bits;\r
+    Parity _parity;\r
+    \r
+    FunctionPointer fpointer[2];\r
+    \r
+    //rx\r
+    void rx_gpio_irq_handler(void);\r
+    void rx_handler(void);\r
+    int read_buffer, rx_bit;\r
+    volatile int out_buffer;\r
+    volatile bool out_valid;\r
+    bool rx_error;\r
+    FlexTicker rxticker;\r
+    \r
+    //tx\r
+    void tx_handler(void);\r
+    void prepare_tx(int c);\r
+    FlexTicker txticker;\r
+    int _char;\r
+    volatile int tx_bit;\r
+    \r
+    \r
+    \r
+    virtual int _getc();\r
+    virtual int _putc(int c);\r
+};\r
+\r
+\r
+#endif\r
+\r
diff --git a/src/libs/SoftSerial/SoftSerial_Ticker.h b/src/libs/SoftSerial/SoftSerial_Ticker.h
new file mode 100644 (file)
index 0000000..cdb5787
--- /dev/null
@@ -0,0 +1,45 @@
+/* \r
+ * SoftSerial by Erik Olieman\r
+ * Date: 05 Jul 2014\r
+ * Revision: 10:236fce2e5b8c\r
+ * URL: http://developer.mbed.org/users/Sissors/code/SoftSerial/\r
+ */\r
+\r
+//A modified version of the regular ticker/timeout libraries to allow us to do timeout without losing accuracy\r
+\r
+#ifndef FLEXTICKER_H\r
+#define FLEXTICKER_H\r
+\r
+#include "mbed.h"\r
+\r
+class FlexTicker: public TimerEvent {\r
+    public:\r
+    template<typename T>\r
+    void attach(T* tptr, void (T::*mptr)(void)) {\r
+        _function.attach(tptr, mptr);\r
+    }\r
\r
+    /** Detach the function\r
+     */\r
+    void detach() {\r
+        remove();\r
+    }\r
+    \r
+    void setNext(int delay) {\r
+        insert(event.timestamp + delay);\r
+    }\r
+    \r
+    void prime(void) {\r
+        event.timestamp = us_ticker_read();\r
+    }\r
\r
+protected:\r
+    virtual void handler() {\r
+        _function.call();\r
+    }\r
\r
+    unsigned int _delay;\r
+    FunctionPointer _function;\r
+};\r
+\r
+#endif\r
diff --git a/src/libs/SoftSerial/SoftSerial_rx.cpp b/src/libs/SoftSerial/SoftSerial_rx.cpp
new file mode 100644 (file)
index 0000000..11d21d5
--- /dev/null
@@ -0,0 +1,91 @@
+/* \r
+ * SoftSerial by Erik Olieman\r
+ * Date: 05 Jul 2014\r
+ * Revision: 10:236fce2e5b8c\r
+ * URL: http://developer.mbed.org/users/Sissors/code/SoftSerial/\r
+ */\r
+\r
+#include "SoftSerial.h"\r
+\r
+uint32_t overhead_us = 200 * 1000000 / SystemCoreClock;         //Random estimation of the overhead of mbed libs, makes slow devices like LPC812 @ 12MHz perform better\r
+\r
+int SoftSerial::_getc( void ) {\r
+    while(!readable());\r
+    out_valid = false;\r
+    return out_buffer;\r
+}\r
+\r
+int SoftSerial::readable(void) {\r
+    return out_valid;\r
+}\r
+\r
+//Start receiving byte\r
+void SoftSerial::rx_gpio_irq_handler(void) {\r
+    rxticker.prime();\r
+    rxticker.setNext(bit_period + (bit_period >> 1) - overhead_us);\r
+    rx->fall(NULL);\r
+    rx_bit = 0;\r
+    rx_error = false;\r
+};    \r
+\r
+void SoftSerial::rx_handler(void) {\r
+    //Receive data\r
+    int val = rx->read();\r
\r
+    rxticker.setNext(bit_period);\r
+    rx_bit++;\r
+    \r
+    \r
+    if (rx_bit <= _bits) {\r
+        read_buffer |= val << (rx_bit - 1);\r
+        return;\r
+    }\r
+    \r
+    //Receive parity\r
+    bool parity_count;\r
+    if (rx_bit == _bits + 1) {\r
+        switch (_parity) {\r
+            case Forced1:\r
+                if (val == 0)\r
+                    rx_error = true;\r
+                return;\r
+            case Forced0:\r
+                if (val == 1)\r
+                    rx_error = true;\r
+                return;\r
+            case Even:\r
+            case Odd:\r
+                parity_count = val;\r
+                for (int i = 0; i<_bits; i++) {\r
+                    if (((read_buffer >> i) & 0x01) == 1)\r
+                        parity_count = !parity_count;\r
+                }\r
+                if ((parity_count) && (_parity == Even))\r
+                    rx_error = true;\r
+                if ((!parity_count) && (_parity == Odd))\r
+                    rx_error = true;\r
+                return;\r
+        }\r
+    }\r
+    \r
+    //Receive stop\r
+    if (rx_bit < _bits + (bool)_parity + _stop_bits) {\r
+        if (!val)\r
+            rx_error = true;\r
+        return;\r
+    }    \r
+    \r
+    //The last stop bit\r
+    if (!val)\r
+        rx_error = true;\r
+    \r
+    if (!rx_error) {\r
+        out_valid = true;\r
+        out_buffer = read_buffer;\r
+        fpointer[RxIrq].call();\r
+    }\r
+    read_buffer = 0;\r
+    rxticker.detach(); \r
+    rx->fall(this, &SoftSerial::rx_gpio_irq_handler);\r
+}\r
+\r
diff --git a/src/libs/SoftSerial/SoftSerial_tx.cpp b/src/libs/SoftSerial/SoftSerial_tx.cpp
new file mode 100644 (file)
index 0000000..eda2307
--- /dev/null
@@ -0,0 +1,87 @@
+/* \r
+ * SoftSerial by Erik Olieman\r
+ * Date: 05 Jul 2014\r
+ * Revision: 10:236fce2e5b8c\r
+ * URL: http://developer.mbed.org/users/Sissors/code/SoftSerial/\r
+ */\r
+\r
+#include "SoftSerial.h"\r
+\r
+int SoftSerial::_putc(int c)\r
+{\r
+    while(!writeable());\r
+    prepare_tx(c);\r
+    tx_bit = 0;\r
+    txticker.prime();\r
+    tx_handler();\r
+    return 0;\r
+}\r
+\r
+void SoftSerial::send_break(void) {\r
+    while(!writeable());\r
+    tx_bit = 0;         //Just to make sure it appears as non-writable to other threads/IRQs\r
+    tx->write(0);\r
+    wait_us((bit_period * _total_bits * 3) / 2);\r
+    tx->write(1);\r
+    tx_bit = -1;\r
+}\r
+\r
+int SoftSerial::writeable(void)\r
+{\r
+    if (!tx_en)\r
+        return false;\r
+    if (tx_bit == -1)\r
+        return true;\r
+    return false;\r
+}\r
+\r
+void SoftSerial::tx_handler(void)\r
+{\r
+    if (tx_bit == _total_bits) {\r
+        tx_bit = -1;\r
+        fpointer[TxIrq].call();\r
+        return;\r
+    }\r
+\r
+    //Flip output\r
+    int cur_out = tx->read();\r
+    tx->write(!cur_out);\r
+\r
+    //Calculate when to do it again\r
+    int count = bit_period;\r
+    tx_bit++;\r
+    while(((_char >> tx_bit) & 0x01) == !cur_out) {\r
+        count+=bit_period;\r
+        tx_bit++;\r
+    }\r
+\r
+    txticker.setNext(count);\r
+}\r
+\r
+void SoftSerial::prepare_tx(int c)\r
+{\r
+    _char = c << 1;\r
+\r
+    bool parity;\r
+    switch (_parity) {\r
+        case Forced1:\r
+            _char |= 1 << (_bits + 1);\r
+        case Even:\r
+            parity = false;\r
+            for (int i = 0; i<_bits; i++) {\r
+                if (((_char >> i) & 0x01) == 1)\r
+                    parity = !parity;\r
+            }\r
+            _char |= parity << (_bits + 1);\r
+        case Odd:\r
+            parity = true;\r
+            for (int i = 0; i<_bits; i++) {\r
+                if (((_char >> i) & 0x01) == 1)\r
+                    parity = !parity;\r
+            }\r
+            _char |= parity << (_bits + 1);\r
+    }\r
+    \r
+    _char |= 0xFFFF << (1 + _bits + (bool)_parity);\r
+    _char &= ~(1<<_total_bits);\r
+}\r
index cc8cae3..2736405 100644 (file)
@@ -8,7 +8,8 @@
 #include "libs/Kernel.h"
 
 #include "modules/tools/laser/Laser.h"
-#include "modules/tools/spindle/Spindle.h"
+#include "modules/tools/spindle/SpindleMaker.h"
+//#include "modules/tools/spindle/SpindleModbus.h"
 #include "modules/tools/extruder/ExtruderMaker.h"
 #include "modules/tools/temperaturecontrol/TemperatureControlPool.h"
 #include "modules/tools/endstops/Endstops.h"
@@ -164,7 +165,11 @@ void init() {
     kernel->add_module( new Laser() );
     #endif
     #ifndef NO_TOOLS_SPINDLE
-    kernel->add_module( new Spindle() );
+    SpindleMaker *sm= new SpindleMaker();
+    sm->load_spindle();
+    delete sm;
+    //kernel->add_module( new Spindle() );
+    //kernel->add_module( new SpindleModbus() );
     #endif
     #ifndef NO_UTILS_PANEL
     kernel->add_module( new Panel() );
diff --git a/src/modules/tools/spindle/AnalogSpindleControl.cpp b/src/modules/tools/spindle/AnalogSpindleControl.cpp
new file mode 100644 (file)
index 0000000..d2013ef
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+      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/Pin.h"
+#include "AnalogSpindleControl.h"
+#include "Config.h"
+#include "checksumm.h"
+#include "ConfigValue.h"
+#include "StreamOutputPool.h"
+#include "PwmOut.h"
+
+#define spindle_checksum                    CHECKSUM("spindle")
+#define spindle_max_rpm_checksum            CHECKSUM("max_rpm")
+#define spindle_pwm_pin_checksum            CHECKSUM("pwm_pin")
+#define spindle_pwm_period_checksum         CHECKSUM("pwm_period")
+#define spindle_switch_on_pin_checksum      CHECKSUM("switch_on_pin")
+
+void AnalogSpindleControl::on_module_loaded()
+{
+
+    spindle_on = false;
+    target_rpm = 0;
+    max_rpm = THEKERNEL->config->value(spindle_checksum, spindle_max_rpm_checksum)->by_default(5000)->as_int();
+
+    // Get the pin for hardware pwm
+    {
+        Pin *smoothie_pin = new Pin();
+        smoothie_pin->from_string(THEKERNEL->config->value(spindle_checksum, spindle_pwm_pin_checksum)->by_default("nc")->as_string());
+        pwm_pin = smoothie_pin->as_output()->hardware_pwm();
+        output_inverted = smoothie_pin->inverting;
+        delete smoothie_pin;
+    }
+    
+    if (pwm_pin == NULL)
+    {
+        THEKERNEL->streams->printf("Error: Spindle PWM pin must be P2.0-2.5 or other PWM pin\n");
+        delete this;
+        return;
+    }
+    
+    int period = THEKERNEL->config->value(spindle_checksum, spindle_pwm_period_checksum)->by_default(1000)->as_int();
+    pwm_pin->period_us(period);
+    pwm_pin->write(output_inverted ? 1 : 0);
+
+    // Get digital out pin for switching the VFD on and off (wired to a digital input on the VFD)
+    std::string switch_on_pin = THEKERNEL->config->value(spindle_checksum, spindle_switch_on_pin_checksum)->by_default("nc")->as_string();
+    switch_on = new Pin();
+    switch_on->from_string(switch_on_pin)->as_output()->set(false);
+   
+
+    register_for_event(ON_GCODE_RECEIVED);
+    register_for_event(ON_GCODE_EXECUTE);
+
+}
+
+void AnalogSpindleControl::turn_on() 
+{
+    
+    switch_on->set(true); 
+    spindle_on = true;
+
+}
+
+
+void AnalogSpindleControl::turn_off() 
+{
+    switch_on->set(false); 
+    spindle_on = false;
+    update_pwm(0);
+
+}
+
+
+void AnalogSpindleControl::set_speed(int rpm) 
+{
+
+    if(rpm < 0) {
+        target_rpm = 0;
+    } else if (rpm > max_rpm) {
+        target_rpm = max_rpm;
+    } else {
+        target_rpm = rpm;
+    }
+    update_pwm(1.0f / max_rpm * target_rpm);
+
+}
+
+
+void AnalogSpindleControl::report_speed() 
+{
+
+    THEKERNEL->streams->printf("Current RPM: %d Analog value: %5.3f\n",
+                               target_rpm, (1.0f / max_rpm * target_rpm));
+
+}
+
+
+void AnalogSpindleControl::update_pwm(float value) 
+{
+
+    if (output_inverted)
+        pwm_pin->write(1.0f - value);
+    else
+        pwm_pin->write(value);
+
+}
+
diff --git a/src/modules/tools/spindle/AnalogSpindleControl.h b/src/modules/tools/spindle/AnalogSpindleControl.h
new file mode 100644 (file)
index 0000000..6a2cdeb
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+      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 ANALOG_SPINDLE_MODULE_H
+#define ANALOG_SPINDLE_MODULE_H
+
+#include "SpindleControl.h"
+
+namespace mbed {
+    class PwmOut;
+}
+
+class Pin;
+
+// This module implements closed loop PID control for spindle RPM.
+class AnalogSpindleControl: public SpindleControl {
+    public:
+        AnalogSpindleControl() {};
+        virtual ~AnalogSpindleControl() {};
+        void on_module_loaded();
+        
+    private:
+       
+        Pin *switch_on; // digital output for switching the VFD on 
+        mbed::PwmOut *pwm_pin; // PWM output for spindle speed control
+        bool output_inverted;
+        
+        int target_rpm;
+        int max_rpm;
+
+        void turn_on(void);
+        void turn_off(void);
+        void set_speed(int);
+        void report_speed(void);
+        void update_pwm(float); 
+};
+
+#endif
+
diff --git a/src/modules/tools/spindle/HuanyangSpindleControl.cpp b/src/modules/tools/spindle/HuanyangSpindleControl.cpp
new file mode 100644 (file)
index 0000000..c2ce19c
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+      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 "ModbusSpindleControl.h"
+#include "HuanyangSpindleControl.h"
+#include "gpio.h"
+#include "Modbus.h"
+
+// Examples:
+// https://github.com/bebro/linuxcnc-huanyang-vfd/blob/emc2/hy_modbus.c
+
+void HuanyangSpindleControl::turn_on() 
+{
+
+    char turn_on_msg[6] = { 0x01, 0x03, 0x01, 0x01, 0x00, 0x00 };
+    unsigned int crc = modbus->crc16(turn_on_msg, 4);
+    turn_on_msg[4] = crc & 0xFF;
+    turn_on_msg[5] = (crc >> 8);
+
+    // Send cippled Modbus telegram for spindle ON
+    modbus->dir_output->set();
+    modbus->delay(1);
+    modbus->serial->write(turn_on_msg, 6);
+    modbus->delay((int) ceil(6 * modbus->delay_time));
+    modbus->dir_output->clear();
+    modbus->delay(50);
+    spindle_on = true;
+
+}
+
+void HuanyangSpindleControl::turn_off() 
+{
+
+    char turn_off_msg[6] = { 0x01, 0x03, 0x01, 0x08, 0x00, 0x00 };
+    unsigned int crc = modbus->crc16(turn_off_msg, 4);
+    turn_off_msg[4] = crc & 0xFF;
+    turn_off_msg[5] = (crc >> 8);
+
+    // Send Modbus telegram for spindle OFF
+    modbus->dir_output->set();
+    modbus->delay(1);
+    modbus->serial->write(turn_off_msg, 6);
+    modbus->delay((int) ceil(6 * modbus->delay_time));
+    modbus->dir_output->clear();
+    modbus->delay(50);
+    spindle_on = false;
+
+}
+
+void HuanyangSpindleControl::set_speed(int target_rpm) 
+{
+
+    // sizeof(set_speed_msg) verwenden
+    //char set_speed_msg[8] = { 0x01, 0x02, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00 };
+    char set_speed_msg[7] = { 0x01, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00 };
+    // convert RPM into Hz
+    unsigned int hz = target_rpm / 60 * 100; 
+    set_speed_msg[3] = (hz >> 8);
+    set_speed_msg[4] = hz & 0xFF;
+    
+    unsigned int crc = modbus->crc16(set_speed_msg, sizeof(set_speed_msg)-2);
+    set_speed_msg[5] = crc & 0xFF;
+    set_speed_msg[6] = (crc >> 8);
+
+    // Send Modbus telegram for spindle speed change
+    modbus->dir_output->set();
+    modbus->delay(1);
+    modbus->serial->write(set_speed_msg, sizeof(set_speed_msg));
+    modbus->delay((int) ceil(sizeof(set_speed_msg) * modbus->delay_time));
+    modbus->dir_output->clear();
+    modbus->delay(50);
+
+}
+
+void HuanyangSpindleControl::report_speed() 
+{
+
+    // TODO: implement this
+
+}
diff --git a/src/modules/tools/spindle/HuanyangSpindleControl.h b/src/modules/tools/spindle/HuanyangSpindleControl.h
new file mode 100644 (file)
index 0000000..56c43c9
--- /dev/null
@@ -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 HUANYANG_SPINDLE_CONTROL_MODULE_H
+#define HUANYANG_SPINDLE_CONTROL_MODULE_H
+
+#include "ModbusSpindleControl.h"
+#include <stdint.h>
+
+// This module implements Modbus control for spindle control over Modbus.
+class HuanyangSpindleControl: public ModbusSpindleControl {
+    public:
+        HuanyangSpindleControl() {};
+        virtual ~HuanyangSpindleControl() {};
+        
+    private:
+        
+        void turn_on(void);
+        void turn_off(void);
+        void set_speed(int);
+        void report_speed(void);
+};
+
+#endif
+
diff --git a/src/modules/tools/spindle/ModbusSpindleControl.cpp b/src/modules/tools/spindle/ModbusSpindleControl.cpp
new file mode 100644 (file)
index 0000000..326c50e
--- /dev/null
@@ -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 "libs/Module.h"
+#include "libs/Kernel.h"
+#include "libs/Pin.h"
+#include "Modbus.h"
+#include "Config.h"
+#include "checksumm.h"
+#include "ConfigValue.h"
+#include "ModbusSpindleControl.h"
+
+#define spindle_checksum                    CHECKSUM("spindle")
+#define spindle_rx_pin_checksum             CHECKSUM("rx_pin")
+#define spindle_tx_pin_checksum             CHECKSUM("tx_pin")
+#define spindle_dir_pin_checksum            CHECKSUM("dir_pin")
+
+void ModbusSpindleControl::on_module_loaded()
+{
+
+    spindle_on = false;
+    PinName rx_pin;
+    PinName tx_pin;
+    PinName dir_pin;
+    
+    // getting the mbed pin names for the soft serial connection
+    {
+        Pin *smoothie_pin = new Pin();
+        smoothie_pin->from_string(THEKERNEL->config->value(spindle_checksum, spindle_rx_pin_checksum)->by_default("nc")->as_string());
+        smoothie_pin->as_input();
+        rx_pin = port_pin((PortName)smoothie_pin->port_number, smoothie_pin->pin);
+        
+        smoothie_pin->from_string(THEKERNEL->config->value(spindle_checksum, spindle_tx_pin_checksum)->by_default("nc")->as_string());
+        smoothie_pin->as_input();
+        tx_pin = port_pin((PortName)smoothie_pin->port_number, smoothie_pin->pin);
+        
+        smoothie_pin->from_string(THEKERNEL->config->value(spindle_checksum, spindle_dir_pin_checksum)->by_default("nc")->as_string());
+        smoothie_pin->as_input();
+        dir_pin = port_pin((PortName)smoothie_pin->port_number, smoothie_pin->pin);
+
+        delete smoothie_pin;
+    }
+
+    modbus = new Modbus(tx_pin, rx_pin, dir_pin);
+
+    register_for_event(ON_GCODE_RECEIVED);
+    register_for_event(ON_GCODE_EXECUTE);
+}
+
diff --git a/src/modules/tools/spindle/ModbusSpindleControl.h b/src/modules/tools/spindle/ModbusSpindleControl.h
new file mode 100644 (file)
index 0000000..23c427d
--- /dev/null
@@ -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 MODBUS_SPINDLE_CONTROL_MODULE_H
+#define MODBUS_SPINDLE_CONTROL_MODULE_H
+
+#include "SpindleControl.h"
+#include <stdint.h>
+
+class Modbus;
+
+// This module implements Modbus control for spindle control over Modbus.
+class ModbusSpindleControl: public SpindleControl {
+    public:
+        ModbusSpindleControl() {};
+        virtual ~ModbusSpindleControl() {};
+        void on_module_loaded();
+        
+        Modbus* modbus;
+        
+        virtual void turn_on(void);
+        virtual void turn_off(void);
+        virtual void set_speed(int);
+        virtual void report_speed(void);
+
+};
+
+#endif
+
similarity index 50%
rename from src/modules/tools/spindle/Spindle.cpp
rename to src/modules/tools/spindle/PWMSpindleControl.cpp
index 24c19f9..33fb006 100644 (file)
@@ -7,7 +7,7 @@
 
 #include "libs/Module.h"
 #include "libs/Kernel.h"
-#include "Spindle.h"
+#include "PWMSpindleControl.h"
 #include "Config.h"
 #include "libs/nuts_bolts.h"
 #include "checksumm.h"
 #include "port_api.h"
 #include "us_ticker_api.h"
 
-#define spindle_enable_checksum          CHECKSUM("spindle_enable")
-#define spindle_pwm_pin_checksum         CHECKSUM("spindle_pwm_pin")
-#define spindle_pwm_period_checksum      CHECKSUM("spindle_pwm_period")
-#define spindle_feedback_pin_checksum    CHECKSUM("spindle_feedback_pin")
-#define spindle_pulses_per_rev_checksum  CHECKSUM("spindle_pulses_per_rev")
-#define spindle_default_rpm_checksum     CHECKSUM("spindle_default_rpm")
-#define spindle_control_P_checksum       CHECKSUM("spindle_control_P")
-#define spindle_control_I_checksum       CHECKSUM("spindle_control_I")
-#define spindle_control_D_checksum       CHECKSUM("spindle_control_D")
-#define spindle_control_smoothing_checksum CHECKSUM("spindle_control_smoothing")
+#define spindle_checksum                    CHECKSUM("spindle")
+#define spindle_vfd_type_checksum           CHECKSUM("vfd_type")
+#define spindle_pwm_pin_checksum            CHECKSUM("pwm_pin")
+#define spindle_pwm_period_checksum         CHECKSUM("pwm_period")
+#define spindle_feedback_pin_checksum       CHECKSUM("feedback_pin")
+#define spindle_pulses_per_rev_checksum     CHECKSUM("pulses_per_rev")
+#define spindle_default_rpm_checksum        CHECKSUM("default_rpm")
+#define spindle_control_P_checksum          CHECKSUM("control_P")
+#define spindle_control_I_checksum          CHECKSUM("control_I")
+#define spindle_control_D_checksum          CHECKSUM("control_D")
+#define spindle_control_smoothing_checksum  CHECKSUM("control_smoothing")
 
 #define UPDATE_FREQ 1000
 
-Spindle::Spindle()
+PWMSpindleControl::PWMSpindleControl()
 {
 }
 
-void Spindle::on_module_loaded()
+void PWMSpindleControl::on_module_loaded()
 {
+
+    printf("PWM Spindle Control loaded\n");
+
     last_time = 0;
     last_edge = 0;
     current_rpm = 0;
     current_I_value = 0;
     current_pwm_value = 0;
     time_since_update = 0;
-    spindle_on = true;
+    spindle_on = false;
     
-    if (!THEKERNEL->config->value(spindle_enable_checksum)->by_default(false)->as_bool())
-    {
-      delete this; // Spindle control module is disabled
-      return;
-    }
+    pulses_per_rev = THEKERNEL->config->value(spindle_checksum, spindle_pulses_per_rev_checksum)->by_default(1.0f)->as_number();
+    target_rpm = THEKERNEL->config->value(spindle_checksum, spindle_default_rpm_checksum)->by_default(5000.0f)->as_number();
+    control_P_term = THEKERNEL->config->value(spindle_checksum, spindle_control_P_checksum)->by_default(0.0001f)->as_number();
+    control_I_term = THEKERNEL->config->value(spindle_checksum, spindle_control_I_checksum)->by_default(0.0001f)->as_number();
+    control_D_term = THEKERNEL->config->value(spindle_checksum, spindle_control_D_checksum)->by_default(0.0001f)->as_number();
 
-    pulses_per_rev = THEKERNEL->config->value(spindle_pulses_per_rev_checksum)->by_default(1.0f)->as_number();
-    target_rpm = THEKERNEL->config->value(spindle_default_rpm_checksum)->by_default(5000.0f)->as_number();
-    control_P_term = THEKERNEL->config->value(spindle_control_P_checksum)->by_default(0.0001f)->as_number();
-    control_I_term = THEKERNEL->config->value(spindle_control_I_checksum)->by_default(0.0001f)->as_number();
-    control_D_term = THEKERNEL->config->value(spindle_control_D_checksum)->by_default(0.0001f)->as_number();
-    
     // Smoothing value is low pass filter time constant in seconds.
-    float smoothing_time = THEKERNEL->config->value(spindle_control_smoothing_checksum)->by_default(0.1f)->as_number();
+    float smoothing_time = THEKERNEL->config->value(spindle_checksum, spindle_control_smoothing_checksum)->by_default(0.1f)->as_number();
     if (smoothing_time * UPDATE_FREQ < 1.0f)
         smoothing_decay = 1.0f;
     else
         smoothing_decay = 1.0f / (UPDATE_FREQ * smoothing_time);
-    
+
     // Get the pin for hardware pwm
     {
         Pin *smoothie_pin = new Pin();
-        smoothie_pin->from_string(THEKERNEL->config->value(spindle_pwm_pin_checksum)->by_default("nc")->as_string());
-        spindle_pin = smoothie_pin->as_output()->hardware_pwm();
+        smoothie_pin->from_string(THEKERNEL->config->value(spindle_checksum, spindle_pwm_pin_checksum)->by_default("nc")->as_string());
+        pwm_pin = smoothie_pin->as_output()->hardware_pwm();
         output_inverted = smoothie_pin->inverting;
         delete smoothie_pin;
     }
     
-    if (spindle_pin == NULL)
+    if (pwm_pin == NULL)
     {
         THEKERNEL->streams->printf("Error: Spindle PWM pin must be P2.0-2.5 or other PWM pin\n");
         delete this;
         return;
     }
     
-    int period = THEKERNEL->config->value(spindle_pwm_period_checksum)->by_default(1000)->as_int();
-    spindle_pin->period_us(period);
-    spindle_pin->write(output_inverted ? 1 : 0);
-    
+    int period = THEKERNEL->config->value(spindle_checksum, spindle_pwm_period_checksum)->by_default(1000)->as_int();
+    pwm_pin->period_us(period);
+    pwm_pin->write(output_inverted ? 1 : 0);
+
     // Get the pin for interrupt
     {
         Pin *smoothie_pin = new Pin();
-        smoothie_pin->from_string(THEKERNEL->config->value(spindle_feedback_pin_checksum)->by_default("nc")->as_string());
+        smoothie_pin->from_string(THEKERNEL->config->value(spindle_checksum, spindle_feedback_pin_checksum)->by_default("nc")->as_string());
         smoothie_pin->as_input();
         if (smoothie_pin->port_number == 0 || smoothie_pin->port_number == 2)
         {
             PinName pinname = port_pin((PortName)smoothie_pin->port_number, smoothie_pin->pin);
             feedback_pin = new mbed::InterruptIn(pinname);
-            feedback_pin->rise(this, &Spindle::on_pin_rise);
+            feedback_pin->rise(this, &PWMSpindleControl::on_pin_rise);
             NVIC_SetPriority(EINT3_IRQn, 16);
         }
         else
@@ -111,12 +109,14 @@ void Spindle::on_module_loaded()
         delete smoothie_pin;
     }
     
-    THEKERNEL->slow_ticker->attach(UPDATE_FREQ, this, &Spindle::on_update_speed);
+    THEKERNEL->slow_ticker->attach(UPDATE_FREQ, this, &PWMSpindleControl::on_update_speed);
+
     register_for_event(ON_GCODE_RECEIVED);
     register_for_event(ON_GCODE_EXECUTE);
+
 }
 
-void Spindle::on_pin_rise()
+void PWMSpindleControl::on_pin_rise()
 {
     uint32_t timestamp = us_ticker_read();
     last_time = timestamp - last_edge;
@@ -124,7 +124,7 @@ void Spindle::on_pin_rise()
     irq_count++;
 }
 
-uint32_t Spindle::on_update_speed(uint32_t dummy)
+uint32_t PWMSpindleControl::on_update_speed(uint32_t dummy)
 {
     // If we don't get any interrupts for 1 second, set current RPM to 0
     uint32_t new_irq = irq_count;
@@ -172,68 +172,52 @@ uint32_t Spindle::on_update_speed(uint32_t dummy)
     }
     
     if (output_inverted)
-        spindle_pin->write(1.0f - current_pwm_value);
+        pwm_pin->write(1.0f - current_pwm_value);
     else
-        spindle_pin->write(current_pwm_value);
+        pwm_pin->write(current_pwm_value);
     
     return 0;
 }
 
 
-void Spindle::on_gcode_received(void* argument)
-{
-    Gcode *gcode = static_cast<Gcode *>(argument);
-    
-    if (gcode->has_m)
-    {
-        if (gcode->m == 957)
-        {
-            // M957: report spindle speed
-            THEKERNEL->streams->printf("Current RPM: %5.0f  Target RPM: %5.0f  PWM value: %5.3f\n",
-                                       current_rpm, target_rpm, current_pwm_value);
-            gcode->mark_as_taken();
-        }
-        else if (gcode->m == 958)
-        {
-            // M958: set spindle PID parameters
-            if (gcode->has_letter('P'))
-                control_P_term = gcode->get_value('P');
-            if (gcode->has_letter('I'))
-                control_I_term = gcode->get_value('I');
-            if (gcode->has_letter('D'))
-                control_D_term = gcode->get_value('D');
-            THEKERNEL->streams->printf("P: %0.6f I: %0.6f D: %0.6f\n",
-                control_P_term, control_I_term, control_D_term);
-        }
-        else if (gcode->m == 3 || gcode->m == 5)
-        {
-            // M3: Spindle on, M5: Spindle off
-            THEKERNEL->conveyor->append_gcode(gcode);
-            gcode->mark_as_taken();
-        }
-    }
+void PWMSpindleControl::turn_on() {
+    spindle_on = true;
 }
 
-void Spindle::on_gcode_execute(void* argument)
-{
-    Gcode *gcode = static_cast<Gcode *>(argument);
-    
-    if (gcode->has_m)
-    {
-        if (gcode->m == 3)
-        {
-            // M3: Spindle on
-            spindle_on = true;
-            
-            if (gcode->has_letter('S'))
-            {
-                target_rpm = gcode->get_value('S');
-            }
-        }
-        else if (gcode->m == 5)
-        {
-            spindle_on = false;
-        }
-    }
+
+void PWMSpindleControl::turn_off() {
+    spindle_on = false;
+}
+
+
+void PWMSpindleControl::set_speed(int rpm) {
+    target_rpm = rpm;
+}
+
+
+void PWMSpindleControl::report_speed() {
+    THEKERNEL->streams->printf("Current RPM: %5.0f  Target RPM: %5.0f  PWM value: %5.3f\n",
+                               current_rpm, target_rpm, current_pwm_value);
+}
+
+
+void PWMSpindleControl::set_p_term(float p) {
+    control_P_term = p;
+}
+
+
+void PWMSpindleControl::set_i_term(float i) {
+    control_I_term = i;
+}
+
+
+void PWMSpindleControl::set_d_term(float d) {
+    control_D_term = d;
+}
+
+
+void PWMSpindleControl::report_settings() {
+    THEKERNEL->streams->printf("P: %0.6f I: %0.6f D: %0.6f\n",
+                               control_P_term, control_I_term, control_D_term);
 }
 
similarity index 75%
rename from src/modules/tools/spindle/Spindle.h
rename to src/modules/tools/spindle/PWMSpindleControl.h
index dd8951d..2d5285e 100644 (file)
@@ -5,10 +5,10 @@
       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
+#ifndef PWM_SPINDLE_MODULE_H
+#define PWM_SPINDLE_MODULE_H
 
-#include "libs/Module.h"
+#include "SpindleControl.h"
 #include <stdint.h>
 
 namespace mbed {
@@ -17,23 +17,23 @@ namespace mbed {
 }
 
 // This module implements closed loop PID control for spindle RPM.
-class Spindle: public Module {
+class PWMSpindleControl: public SpindleControl {
     public:
-        Spindle();
-        virtual ~Spindle() {};
+        PWMSpindleControl();
+        virtual ~PWMSpindleControl() {};
         void on_module_loaded();
         
-        
     private:
+        
         void on_pin_rise();
-        void on_gcode_received(void *argument);
-        void on_gcode_execute(void *argument);
         uint32_t on_update_speed(uint32_t dummy);
         
-        mbed::PwmOut *spindle_pin; // PWM output for spindle speed control
+        mbed::PwmOut *pwm_pin; // PWM output for spindle speed control
         mbed::InterruptIn *feedback_pin; // Interrupt pin for measuring speed
         bool output_inverted;
-        
+       
+        bool vfd_spindle; // true if we have a VFD driven spindle
+
         // Current values, updated at runtime
         bool spindle_on;
         float current_rpm;
@@ -55,6 +55,15 @@ class Spindle: public Module {
         uint32_t last_edge; // Timestamp of last edge
         volatile uint32_t last_time; // Time delay between last two edges
         volatile uint32_t irq_count;
+        
+        void turn_on(void);
+        void turn_off(void);
+        void set_speed(int);
+        void report_speed(void);
+        void set_p_term(float);
+        void set_i_term(float);
+        void set_d_term(float);
+        void report_settings(void);
 };
 
 #endif
diff --git a/src/modules/tools/spindle/SpindleControl.cpp b/src/modules/tools/spindle/SpindleControl.cpp
new file mode 100644 (file)
index 0000000..eb77114
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+      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 "Gcode.h"
+#include "Conveyor.h"
+#include "SpindleControl.h"
+
+void SpindleControl::on_gcode_received(void *argument) 
+{
+    
+    Gcode *gcode = static_cast<Gcode *>(argument);
+        
+    if (gcode->has_m)
+    {
+        if (gcode->m == 957)
+        {
+            // M957: report spindle speed
+            report_speed();
+            gcode->mark_as_taken();
+        }
+        else if (gcode->m == 958)
+        {
+            // M958: set spindle PID parameters
+            if (gcode->has_letter('P'))
+                set_p_term( gcode->get_value('P') );
+            if (gcode->has_letter('I'))
+                set_p_term( gcode->get_value('I') );
+            if (gcode->has_letter('D'))
+                set_p_term( gcode->get_value('D') );
+            // report PID settings
+            get_pid_settings();
+          
+        }
+        else if (gcode->m == 3 || gcode->m == 5)
+        {
+            // M3: Spindle on, M5: Spindle off
+            THEKERNEL->conveyor->append_gcode(gcode);
+            gcode->mark_as_taken();
+        }
+    }
+
+}
+
+void SpindleControl::on_gcode_execute(void *argument) 
+{
+    
+    Gcode *gcode = static_cast<Gcode *>(argument);
+    
+    if (gcode->has_m)
+    {
+        if (gcode->m == 3)
+        {
+            // M3: Spindle on
+            if(!spindle_on) {
+                turn_on();
+            }
+            
+            // M3 with S value provided: set speed
+            if (gcode->has_letter('S'))
+            {
+                set_speed(gcode->get_value('S'));
+            }
+        }
+        else if (gcode->m == 5)
+        {
+            if(spindle_on) {
+                turn_off();
+            }
+        }
+    }
+
+}
+
diff --git a/src/modules/tools/spindle/SpindleControl.h b/src/modules/tools/spindle/SpindleControl.h
new file mode 100644 (file)
index 0000000..f024625
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+      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_CONTROL_MODULE_H
+#define SPINDLE_CONTROL_MODULE_H
+
+#include "libs/Module.h"
+
+class SpindleControl: public Module {
+    public:
+        SpindleControl() {};
+        virtual ~SpindleControl() {};
+        virtual void on_module_loaded() {};
+
+    protected:
+        bool spindle_on;
+
+    private:
+        void on_gcode_received(void *argument);
+        void on_gcode_execute(void *argument);
+        
+        virtual void turn_on(void) {};
+        virtual void turn_off(void) {};
+        virtual void set_speed(int) {};
+        virtual void report_speed(void) {};
+        virtual void set_p_term(float) {};
+        virtual void set_i_term(float) {};
+        virtual void set_d_term(float) {};
+        virtual void get_pid_settings(void) {};
+};
+
+#endif
diff --git a/src/modules/tools/spindle/SpindleMaker.cpp b/src/modules/tools/spindle/SpindleMaker.cpp
new file mode 100644 (file)
index 0000000..00edd4d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+      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 "SpindleMaker.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "SpindleControl.h"
+#include "PWMSpindleControl.h"
+#include "AnalogSpindleControl.h"
+#include "HuanyangSpindleControl.h"
+#include "Config.h"
+#include "checksumm.h"
+#include "ConfigValue.h"
+#include "StreamOutputPool.h"
+
+#define spindle_checksum            CHECKSUM("spindle")
+#define enable_checksum             CHECKSUM("enable")
+#define spindle_type_checksum       CHECKSUM("type")
+#define spindle_vfd_type_checksum   CHECKSUM("vfd_type")
+
+void SpindleMaker::load_spindle(){
+
+    // If the spindle module is disabled load no Spindle 
+    if( !THEKERNEL->config->value( spindle_checksum, enable_checksum  )->by_default(false)->as_bool() ) {
+        THEKERNEL->streams->printf("NOTE: Spindle Module is disabled\n");
+        return;    
+    }
+
+    spindle = NULL;
+
+    std::string spindle_type = THEKERNEL->config->value( spindle_checksum, spindle_type_checksum )->by_default("pwm")->as_string();
+    std::string vfd_type = THEKERNEL->config->value( spindle_checksum, spindle_vfd_type_checksum )->by_default("none")->as_string(); 
+
+    // check config which spindle type we need
+    if( spindle_type.compare("pwm") == 0 ) {
+        spindle = new PWMSpindleControl();
+    } else if ( spindle_type.compare("analog") == 0 ) {
+        spindle = new AnalogSpindleControl();
+    } else if ( spindle_type.compare("modbus") == 0 ) {
+        if(vfd_type.compare("huanyang") == 0) { 
+            spindle = new HuanyangSpindleControl();
+        } else {
+            delete spindle;
+            THEKERNEL->streams->printf("ERROR: No valid spindle VFD type defined\n");
+        }
+    } else {
+        delete spindle;
+        THEKERNEL->streams->printf("ERROR: No valid spindle type defined\n");
+    }
+    
+    if( spindle != NULL) {
+        THEKERNEL->add_module( spindle );
+    }
+
+}
+
diff --git a/src/modules/tools/spindle/SpindleMaker.h b/src/modules/tools/spindle/SpindleMaker.h
new file mode 100644 (file)
index 0000000..a2066b1
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *       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 SPINDLEMAKER_H
+#define SPINDLERMAKER_H
+
+class SpindleControl;
+
+class SpindleMaker {
+    public:
+        SpindleMaker() {};
+        virtual ~SpindleMaker() {};
+        
+        void load_spindle();
+
+    private:
+        SpindleControl* spindle;
+            
+};
+
+#endif