--- /dev/null
+/*
+ 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;
+
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+\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
--- /dev/null
+\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
--- /dev/null
+/**\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
--- /dev/null
+\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
--- /dev/null
+/* \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
--- /dev/null
+/* \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
--- /dev/null
+/* \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
--- /dev/null
+/* \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
--- /dev/null
+/* \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
#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"
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() );
--- /dev/null
+/*
+ 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);
+
+}
+
--- /dev/null
+/*
+ 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
+
--- /dev/null
+/*
+ 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
+
+}
--- /dev/null
+/*
+ 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
+
--- /dev/null
+/*
+ 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);
+}
+
--- /dev/null
+/*
+ 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
+
#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
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;
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;
}
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);
}
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 {
}
// 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;
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
--- /dev/null
+/*
+ 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();
+ }
+ }
+ }
+
+}
+
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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 );
+ }
+
+}
+
--- /dev/null
+/*
+ * 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