Minor include related compile fix. Added experimental XON/XOFF flow control. Not officially supported!

- A latency issue related to USB-to-serial converters on the Arduino
does not allow for XON/XOFF flow control to work correctly on standard
terminal programs. It seems that only specialized UI's or avoiding the
USB port all together solves this problem. However, XON/XOFF flow
control is added for advanced users only as a compile-time option. This
feature is officially *NOT* supported by grbl, but let us know of any
successes with it!
This commit is contained in:
Sonny Jeon 2012-02-25 09:06:42 -07:00
parent e9b28279db
commit d6abf10d49
3 changed files with 92 additions and 28 deletions

View File

@ -114,6 +114,14 @@
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays. // time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)
// FOR ADVANCED USERS ONLY: Toggles XON/XOFF software flow control for serial communications.
// Officially not supported due to problems involving USB-to-serial chip latency (Atmega8U2/FTDI)
// when connecting to an Arduino through the USB port. This problem has to do with having no control
// of the USB packets and causing standard terminal programs not being able to honor the XON/XOFF
// control characters on time. However, with specially programmed UI's or avoiding the USB interface
// completely, XON/XOFF flow control should work. In any case, please report any successes to grbl
// administrators!
#define ENABLE_XONXOFF 0 // Boolean. Default disabled.
// ----------------------------------------------- // -----------------------------------------------

View File

@ -21,10 +21,11 @@
#ifndef nuts_bolts_h #ifndef nuts_bolts_h
#define nuts_bolts_h #define nuts_bolts_h
#include <config.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "config.h"
#define false 0 #define false 0
#define true 1 #define true 1

103
serial.c
View File

@ -42,6 +42,27 @@ uint8_t tx_buffer[TX_BUFFER_SIZE];
uint8_t tx_buffer_head = 0; uint8_t tx_buffer_head = 0;
volatile uint8_t tx_buffer_tail = 0; volatile uint8_t tx_buffer_tail = 0;
#if ENABLE_XONXOFF
#define RX_BUFFER_FULL 96 // XOFF high watermark
#define RX_BUFFER_LOW 64 // XON low watermark
#define SEND_XOFF 1
#define SEND_XON 2
#define XOFF_SENT 3
#define XON_SENT 4
#define XOFF_CHAR 0x13
#define XON_CHAR 0x11
volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
// Returns the number of bytes in the RX buffer. This replaces a typical byte counter to prevent
// the interrupt and main programs from writing to the counter at the same time.
static uint8_t get_rx_buffer_count()
{
if (rx_buffer_head == rx_buffer_tail) { return(0); }
if (rx_buffer_head < rx_buffer_tail) { return(rx_buffer_tail-rx_buffer_head); }
return (RX_BUFFER_SIZE - (rx_buffer_head-rx_buffer_tail));
}
#endif
static void set_baud_rate(long baud) { static void set_baud_rate(long baud) {
uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1); uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1);
UBRR0H = UBRR0_value >> 8; UBRR0H = UBRR0_value >> 8;
@ -84,21 +105,33 @@ void serial_write(uint8_t data) {
} }
// Data Register Empty Interrupt handler // Data Register Empty Interrupt handler
ISR(USART_UDRE_vect) { ISR(USART_UDRE_vect)
{
// Temporary tx_buffer_tail (to optimize for volatile) // Temporary tx_buffer_tail (to optimize for volatile)
uint8_t tail = tx_buffer_tail; uint8_t tail = tx_buffer_tail;
#if ENABLE_XONXOFF
switch (flow_ctrl) {
case SEND_XOFF: UDR0 = XOFF_CHAR; flow_ctrl = XOFF_SENT; break;
case SEND_XON: UDR0 = XON_CHAR; flow_ctrl = XON_SENT; break;
default:
#endif
// Send a byte from the buffer // Send a byte from the buffer
UDR0 = tx_buffer[tail]; UDR0 = tx_buffer[tail];
// Update tail position // Update tail position
tail ++; tail++;
if (tail == TX_BUFFER_SIZE) { tail = 0; } if (tail == TX_BUFFER_SIZE) { tail = 0; }
tx_buffer_tail = tail;
#if ENABLE_XONXOFF
}
#endif
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer // Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); } if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
tx_buffer_tail = tail;
} }
uint8_t serial_read() uint8_t serial_read()
@ -109,6 +142,14 @@ uint8_t serial_read()
uint8_t data = rx_buffer[rx_buffer_tail]; uint8_t data = rx_buffer[rx_buffer_tail];
rx_buffer_tail++; rx_buffer_tail++;
if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; } if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; }
#if ENABLE_XONXOFF
if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl != XON_SENT) {
flow_ctrl = SEND_XON;
UCSR0B |= (1 << UDRIE0); // Force TX
}
#endif
return data; return data;
} }
} }
@ -116,33 +157,47 @@ uint8_t serial_read()
ISR(USART_RX_vect) ISR(USART_RX_vect)
{ {
uint8_t data = UDR0; uint8_t data = UDR0;
uint8_t next_head = rx_buffer_head + 1; uint8_t next_head;
if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
// Write data to buffer unless it is full. // Pick off runtime command characters directly from the serial stream. These characters are
if (next_head != rx_buffer_tail) { // not passed into the buffer, but these set system state flag bits for runtime execution.
// Pick off runtime command characters directly from the serial stream. These characters are switch (data) {
// not passed into the buffer, but these set system state flag bits for runtime execution. case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true
switch (data) { case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true
case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true
case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true case CMD_RESET:
case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true // Immediately force stepper and spindle subsystem idle at an interrupt level.
case CMD_RESET: if (!(sys.execute & EXEC_RESET)) { // Force stop only first time.
// Immediately force stepper and spindle subsystem idle at an interrupt level. st_go_idle();
if (!(sys.execute & EXEC_RESET)) { // Force stop only first time. spindle_stop();
st_go_idle(); }
spindle_stop(); sys.execute |= EXEC_RESET; // Set as true
} break;
sys.execute |= EXEC_RESET; // Set as true default: // Write character to buffer
break; next_head = rx_buffer_head + 1;
default : // Write character to buffer if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
// Write data to buffer unless it is full.
if (next_head != rx_buffer_tail) {
rx_buffer[rx_buffer_head] = data; rx_buffer[rx_buffer_head] = data;
rx_buffer_head = next_head; rx_buffer_head = next_head;
}
#if ENABLE_XONXOFF
if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl != XOFF_SENT) {
flow_ctrl = SEND_XOFF;
UCSR0B |= (1 << UDRIE0); // Force TX
}
#endif
}
} }
} }
void serial_reset_read_buffer() void serial_reset_read_buffer()
{ {
rx_buffer_tail = rx_buffer_head; rx_buffer_tail = rx_buffer_head;
#if ENABLE_XONXOFF
flow_ctrl = XON_SENT;
#endif
} }