4bdc20ffb9
- Overhauled the state machine and cleaned up its overall operation. This involved creating a new ‘suspend’ state for what all external commands, except real-time commands, are ignored. All hold type states enter this suspend state. - Removed ‘auto cycle start’ setting from Grbl. This was not used by users in its intended way and is somewhat redundant, as GUI manage the cycle start by streaming. It also muddled up how Grbl should interpret how and when to execute a g-code block. Removing it made everything much much simpler. - Fixed a program pause bug when used with other buffer_sync commands. - New safety door feature for OEMs. Immediately forces a feed hold and then de-energizes the machine. Resuming is blocked until the door is closed. When it is, it re-energizes the system and then resumes on the normal toolpath. - Safety door input pin is optional and uses the feed hold pin on A1. Enabled by config.h define. - Spindle and coolant re-energizing upon a safety door resume has a programmable delay time to allow for complete spin up to rpm and turning on the coolant before resuming motion. - Safety door-style feed holds can be used instead of regular feed hold (doesn’t de-energize the machine) with a ‘@‘ character. If the safety door input pin is not enabled, the system can be resumed at any time.
207 lines
6.4 KiB
C
207 lines
6.4 KiB
C
/*
|
|
serial.c - Low level functions for sending and recieving bytes via the serial port
|
|
Part of Grbl v0.9
|
|
|
|
Copyright (c) 2012-2015 Sungeun K. Jeon
|
|
|
|
Grbl is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Grbl is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/*
|
|
This file is based on work from Grbl v0.8, distributed under the
|
|
terms of the MIT-license. See COPYING for more details.
|
|
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
|
Copyright (c) 2011-2012 Sungeun K. Jeon
|
|
*/
|
|
|
|
#include "grbl.h"
|
|
|
|
|
|
uint8_t serial_rx_buffer[RX_BUFFER_SIZE];
|
|
uint8_t serial_rx_buffer_head = 0;
|
|
volatile uint8_t serial_rx_buffer_tail = 0;
|
|
|
|
uint8_t serial_tx_buffer[TX_BUFFER_SIZE];
|
|
uint8_t serial_tx_buffer_head = 0;
|
|
volatile uint8_t serial_tx_buffer_tail = 0;
|
|
|
|
|
|
#ifdef ENABLE_XONXOFF
|
|
volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
|
|
#endif
|
|
|
|
|
|
// Returns the number of bytes used in the RX serial buffer.
|
|
uint8_t serial_get_rx_buffer_count()
|
|
{
|
|
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
|
|
if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); }
|
|
return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head));
|
|
}
|
|
|
|
|
|
// Returns the number of bytes used in the TX serial buffer.
|
|
// NOTE: Not used except for debugging and ensuring no TX bottlenecks.
|
|
uint8_t serial_get_tx_buffer_count()
|
|
{
|
|
uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile
|
|
if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); }
|
|
return (TX_BUFFER_SIZE - (ttail-serial_tx_buffer_head));
|
|
}
|
|
|
|
|
|
void serial_init()
|
|
{
|
|
// Set baud rate
|
|
#if BAUD_RATE < 57600
|
|
uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ;
|
|
UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX
|
|
#else
|
|
uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2;
|
|
UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200
|
|
#endif
|
|
UBRR0H = UBRR0_value >> 8;
|
|
UBRR0L = UBRR0_value;
|
|
|
|
// enable rx and tx
|
|
UCSR0B |= 1<<RXEN0;
|
|
UCSR0B |= 1<<TXEN0;
|
|
|
|
// enable interrupt on complete reception of a byte
|
|
UCSR0B |= 1<<RXCIE0;
|
|
|
|
// defaults to 8-bit, no parity, 1 stop bit
|
|
}
|
|
|
|
|
|
// Writes one byte to the TX serial buffer. Called by main program.
|
|
// TODO: Check if we can speed this up for writing strings, rather than single bytes.
|
|
void serial_write(uint8_t data) {
|
|
// Calculate next head
|
|
uint8_t next_head = serial_tx_buffer_head + 1;
|
|
if (next_head == TX_BUFFER_SIZE) { next_head = 0; }
|
|
|
|
// Wait until there is space in the buffer
|
|
while (next_head == serial_tx_buffer_tail) {
|
|
// TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
|
|
if (sys.rt_exec_state & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
|
|
}
|
|
|
|
// Store data and advance head
|
|
serial_tx_buffer[serial_tx_buffer_head] = data;
|
|
serial_tx_buffer_head = next_head;
|
|
|
|
// Enable Data Register Empty Interrupt to make sure tx-streaming is running
|
|
UCSR0B |= (1 << UDRIE0);
|
|
}
|
|
|
|
|
|
// Data Register Empty Interrupt handler
|
|
ISR(SERIAL_UDRE)
|
|
{
|
|
uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
|
|
|
|
#ifdef ENABLE_XONXOFF
|
|
if (flow_ctrl == SEND_XOFF) {
|
|
UDR0 = XOFF_CHAR;
|
|
flow_ctrl = XOFF_SENT;
|
|
} else if (flow_ctrl == SEND_XON) {
|
|
UDR0 = XON_CHAR;
|
|
flow_ctrl = XON_SENT;
|
|
} else
|
|
#endif
|
|
{
|
|
// Send a byte from the buffer
|
|
UDR0 = serial_tx_buffer[tail];
|
|
|
|
// Update tail position
|
|
tail++;
|
|
if (tail == TX_BUFFER_SIZE) { tail = 0; }
|
|
|
|
serial_tx_buffer_tail = tail;
|
|
}
|
|
|
|
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
|
|
if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
|
|
}
|
|
|
|
|
|
// Fetches the first byte in the serial read buffer. Called by main program.
|
|
uint8_t serial_read()
|
|
{
|
|
uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile)
|
|
if (serial_rx_buffer_head == tail) {
|
|
return SERIAL_NO_DATA;
|
|
} else {
|
|
uint8_t data = serial_rx_buffer[tail];
|
|
|
|
tail++;
|
|
if (tail == RX_BUFFER_SIZE) { tail = 0; }
|
|
serial_rx_buffer_tail = tail;
|
|
|
|
#ifdef ENABLE_XONXOFF
|
|
if ((serial_get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
|
|
flow_ctrl = SEND_XON;
|
|
UCSR0B |= (1 << UDRIE0); // Force TX
|
|
}
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
}
|
|
|
|
|
|
ISR(SERIAL_RX)
|
|
{
|
|
uint8_t data = UDR0;
|
|
uint8_t next_head;
|
|
|
|
// Pick off realtime command characters directly from the serial stream. These characters are
|
|
// not passed into the buffer, but these set system state flag bits for realtime execution.
|
|
switch (data) {
|
|
case CMD_STATUS_REPORT: bit_true_atomic(sys.rt_exec_state, EXEC_STATUS_REPORT); break; // Set as true
|
|
case CMD_CYCLE_START: bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); break; // Set as true
|
|
case CMD_FEED_HOLD: bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD); break; // Set as true
|
|
case CMD_SAFETY_DOOR: bit_true_atomic(sys.rt_exec_state, EXEC_SAFETY_DOOR); break; // Set as true
|
|
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
|
|
default: // Write character to buffer
|
|
next_head = serial_rx_buffer_head + 1;
|
|
if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
|
|
|
|
// Write data to buffer unless it is full.
|
|
if (next_head != serial_rx_buffer_tail) {
|
|
serial_rx_buffer[serial_rx_buffer_head] = data;
|
|
serial_rx_buffer_head = next_head;
|
|
|
|
#ifdef ENABLE_XONXOFF
|
|
if ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
|
|
flow_ctrl = SEND_XOFF;
|
|
UCSR0B |= (1 << UDRIE0); // Force TX
|
|
}
|
|
#endif
|
|
|
|
}
|
|
//TODO: else alarm on overflow?
|
|
}
|
|
}
|
|
|
|
|
|
void serial_reset_read_buffer()
|
|
{
|
|
serial_rx_buffer_tail = serial_rx_buffer_head;
|
|
|
|
#ifdef ENABLE_XONXOFF
|
|
flow_ctrl = XON_SENT;
|
|
#endif
|
|
}
|