2009-01-25 00:48:56 +01:00
|
|
|
/*
|
2011-06-03 15:31:59 +02:00
|
|
|
serial.c - Low level functions for sending and recieving bytes via the serial port
|
|
|
|
Part of Grbl
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-06-03 15:31:59 +02:00
|
|
|
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
2012-01-29 04:41:08 +01:00
|
|
|
Copyright (c) 2011-2012 Sungeun K. Jeon
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-06-03 15:31:59 +02:00
|
|
|
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.
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-06-03 15:31:59 +02:00
|
|
|
Grbl is distributed in the hope that it will be useful,
|
2009-01-25 00:48:56 +01:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2011-06-03 15:31:59 +02:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-06-03 15:31:59 +02:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
2009-01-25 00:48:56 +01:00
|
|
|
*/
|
|
|
|
|
2011-06-03 15:31:59 +02:00
|
|
|
/* This code was initially inspired by the wiring_serial module by David A. Mellis which
|
|
|
|
used to be a part of the Arduino project. */
|
|
|
|
|
2011-02-04 21:10:17 +01:00
|
|
|
#include <avr/interrupt.h>
|
2011-06-03 15:28:14 +02:00
|
|
|
#include <avr/sleep.h>
|
2011-06-03 21:50:02 +02:00
|
|
|
#include "serial.h"
|
2011-12-09 02:47:48 +01:00
|
|
|
#include "config.h"
|
2012-10-22 03:18:24 +02:00
|
|
|
#include "motion_control.h"
|
2011-12-09 02:47:48 +01:00
|
|
|
#include "nuts_bolts.h"
|
|
|
|
#include "protocol.h"
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
#define RX_BUFFER_SIZE 128
|
2012-01-06 18:10:41 +01:00
|
|
|
#define TX_BUFFER_SIZE 64
|
2011-05-31 22:29:50 +02:00
|
|
|
|
2011-06-01 09:45:15 +02:00
|
|
|
uint8_t rx_buffer[RX_BUFFER_SIZE];
|
|
|
|
uint8_t rx_buffer_head = 0;
|
|
|
|
uint8_t rx_buffer_tail = 0;
|
|
|
|
|
|
|
|
uint8_t tx_buffer[TX_BUFFER_SIZE];
|
|
|
|
uint8_t tx_buffer_head = 0;
|
|
|
|
volatile uint8_t tx_buffer_tail = 0;
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2012-10-01 03:57:10 +02:00
|
|
|
#ifdef ENABLE_XONXOFF
|
2012-02-25 17:06:42 +01:00
|
|
|
#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
|
|
|
|
|
2011-06-05 20:54:23 +02:00
|
|
|
static void set_baud_rate(long baud) {
|
|
|
|
uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1);
|
2011-12-09 02:47:48 +01:00
|
|
|
UBRR0H = UBRR0_value >> 8;
|
|
|
|
UBRR0L = UBRR0_value;
|
2011-06-05 20:54:23 +02:00
|
|
|
}
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-06-03 15:28:14 +02:00
|
|
|
void serial_init(long baud)
|
2009-01-25 00:48:56 +01:00
|
|
|
{
|
2011-06-05 20:54:23 +02:00
|
|
|
set_baud_rate(baud);
|
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
/* baud doubler off - Only needed on Uno XXX */
|
2011-02-11 08:25:49 +01:00
|
|
|
UCSR0A &= ~(1 << U2X0);
|
2011-02-11 01:11:33 +01:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// enable rx and tx
|
2011-02-04 21:10:17 +01:00
|
|
|
UCSR0B |= 1<<RXEN0;
|
|
|
|
UCSR0B |= 1<<TXEN0;
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// enable interrupt on complete reception of a byte
|
2011-02-04 21:10:17 +01:00
|
|
|
UCSR0B |= 1<<RXCIE0;
|
2011-06-03 15:28:14 +02:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// defaults to 8-bit, no parity, 1 stop bit
|
2009-01-25 00:48:56 +01:00
|
|
|
}
|
|
|
|
|
2011-06-03 15:28:14 +02:00
|
|
|
void serial_write(uint8_t data) {
|
|
|
|
// Calculate next head
|
2011-09-16 04:32:15 +02:00
|
|
|
uint8_t next_head = tx_buffer_head + 1;
|
|
|
|
if (next_head == TX_BUFFER_SIZE) { next_head = 0; }
|
2009-01-25 00:48:56 +01:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Wait until there is space in the buffer
|
|
|
|
while (next_head == tx_buffer_tail) {
|
2012-01-10 16:34:48 +01:00
|
|
|
if (sys.execute & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
|
2011-12-09 02:47:48 +01:00
|
|
|
}
|
2011-05-31 22:29:50 +02:00
|
|
|
|
2011-06-03 15:28:14 +02:00
|
|
|
// Store data and advance head
|
2011-06-01 09:45:15 +02:00
|
|
|
tx_buffer[tx_buffer_head] = data;
|
|
|
|
tx_buffer_head = next_head;
|
2011-06-03 15:28:14 +02:00
|
|
|
|
2011-06-03 15:42:28 +02:00
|
|
|
// Enable Data Register Empty Interrupt to make sure tx-streaming is running
|
2011-12-09 02:47:48 +01:00
|
|
|
UCSR0B |= (1 << UDRIE0);
|
2011-05-31 22:29:50 +02:00
|
|
|
}
|
|
|
|
|
2011-06-03 15:28:14 +02:00
|
|
|
// Data Register Empty Interrupt handler
|
2012-02-25 17:06:42 +01:00
|
|
|
ISR(USART_UDRE_vect)
|
|
|
|
{
|
2011-12-09 02:47:48 +01:00
|
|
|
// Temporary tx_buffer_tail (to optimize for volatile)
|
2011-06-01 09:45:15 +02:00
|
|
|
uint8_t tail = tx_buffer_tail;
|
2012-02-25 17:06:42 +01:00
|
|
|
|
2012-10-01 03:57:10 +02:00
|
|
|
#ifdef ENABLE_XONXOFF
|
2012-03-05 20:01:02 +01:00
|
|
|
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
|
2012-02-25 17:06:42 +01:00
|
|
|
#endif
|
2012-03-05 20:01:02 +01:00
|
|
|
{
|
|
|
|
// Send a byte from the buffer
|
|
|
|
UDR0 = tx_buffer[tail];
|
2012-02-25 17:06:42 +01:00
|
|
|
|
2012-03-05 20:01:02 +01:00
|
|
|
// Update tail position
|
|
|
|
tail++;
|
|
|
|
if (tail == TX_BUFFER_SIZE) { tail = 0; }
|
|
|
|
|
|
|
|
tx_buffer_tail = tail;
|
|
|
|
}
|
2012-02-25 17:06:42 +01:00
|
|
|
|
2011-06-03 15:42:28 +02:00
|
|
|
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
|
2011-06-03 15:31:59 +02:00
|
|
|
if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
|
2009-01-25 00:48:56 +01:00
|
|
|
}
|
|
|
|
|
2011-06-03 15:28:14 +02:00
|
|
|
uint8_t serial_read()
|
2009-01-25 00:48:56 +01:00
|
|
|
{
|
2011-12-09 02:47:48 +01:00
|
|
|
if (rx_buffer_head == rx_buffer_tail) {
|
|
|
|
return SERIAL_NO_DATA;
|
|
|
|
} else {
|
|
|
|
uint8_t data = rx_buffer[rx_buffer_tail];
|
|
|
|
rx_buffer_tail++;
|
|
|
|
if (rx_buffer_tail == RX_BUFFER_SIZE) { rx_buffer_tail = 0; }
|
2012-02-25 17:06:42 +01:00
|
|
|
|
2012-10-01 03:57:10 +02:00
|
|
|
#ifdef ENABLE_XONXOFF
|
2012-03-05 20:01:02 +01:00
|
|
|
if ((get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
|
2012-02-25 17:06:42 +01:00
|
|
|
flow_ctrl = SEND_XON;
|
|
|
|
UCSR0B |= (1 << UDRIE0); // Force TX
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
return data;
|
|
|
|
}
|
2009-01-25 00:48:56 +01:00
|
|
|
}
|
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
ISR(USART_RX_vect)
|
2009-01-25 00:48:56 +01:00
|
|
|
{
|
2011-12-09 02:47:48 +01:00
|
|
|
uint8_t data = UDR0;
|
2012-02-25 17:06:42 +01:00
|
|
|
uint8_t next_head;
|
|
|
|
|
|
|
|
// Pick off runtime command characters directly from the serial stream. These characters are
|
|
|
|
// not passed into the buffer, but these set system state flag bits for runtime execution.
|
|
|
|
switch (data) {
|
|
|
|
case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true
|
|
|
|
case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true
|
|
|
|
case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true
|
Hard limits, homing direction, pull-off limits after homing, status reports in mm or inches, system alarm, and more.
- Thank you statement added for Alden Hart of Synthetos.
- Hard limits option added, which also works with homing by pulling off
the switches to help prevent unintended triggering. Hard limits use a
interrupt to sense a falling edge pin change and immediately go into
alarm mode, which stops everything and forces the user to issue a reset
(Ctrl-x) or reboot.
- Auto cycle start now a configuration option.
- Alarm mode: A new method to kill all Grbl processes in the event of
something catastrophic or potentially catastropic. Just works with hard
limits for now, but will be expanded to include g-code errors (most
likely) and other events.
- Updated status reports to be configurable in inches or mm mode. Much
more to do here, but this is the first step.
- New settings: auto cycle start, hard limit enable, homing direction
mask (which works the same as the stepper mask), homing pulloff
distance (or distance traveled from homed machine zero to prevent
accidental limit trip).
- Minor memory liberation and calculation speed ups.
2012-10-17 05:29:45 +02:00
|
|
|
case CMD_RESET:
|
2012-02-25 17:06:42 +01:00
|
|
|
// Immediately force stepper and spindle subsystem idle at an interrupt level.
|
2012-11-01 16:37:27 +01:00
|
|
|
mc_alarm();
|
2012-02-25 17:06:42 +01:00
|
|
|
sys.execute |= EXEC_RESET; // Set as true
|
|
|
|
break;
|
|
|
|
default: // Write character to buffer
|
|
|
|
next_head = rx_buffer_head + 1;
|
|
|
|
if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
|
|
|
|
|
|
|
|
// Write data to buffer unless it is full.
|
|
|
|
if (next_head != rx_buffer_tail) {
|
2011-12-09 02:47:48 +01:00
|
|
|
rx_buffer[rx_buffer_head] = data;
|
2012-02-25 17:06:42 +01:00
|
|
|
rx_buffer_head = next_head;
|
|
|
|
|
2012-10-01 03:57:10 +02:00
|
|
|
#ifdef ENABLE_XONXOFF
|
2012-03-05 20:01:02 +01:00
|
|
|
if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
|
2012-02-25 17:06:42 +01:00
|
|
|
flow_ctrl = SEND_XOFF;
|
|
|
|
UCSR0B |= (1 << UDRIE0); // Force TX
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
2011-12-09 02:47:48 +01:00
|
|
|
}
|
2009-01-25 00:48:56 +01:00
|
|
|
}
|
2011-12-09 02:47:48 +01:00
|
|
|
|
|
|
|
void serial_reset_read_buffer()
|
|
|
|
{
|
|
|
|
rx_buffer_tail = rx_buffer_head;
|
2012-02-25 17:06:42 +01:00
|
|
|
|
2012-10-01 03:57:10 +02:00
|
|
|
#ifdef ENABLE_XONXOFF
|
2012-02-25 17:06:42 +01:00
|
|
|
flow_ctrl = XON_SENT;
|
|
|
|
#endif
|
2012-03-10 20:34:09 +01:00
|
|
|
}
|