Merge pull request #2994 from AnHardt/Inerrup-save-serial

Interrupt safe serial --- Guard against non-atomic variable changes from interrupt routines
This commit is contained in:
Scott Lahteine 2016-03-05 22:25:12 -08:00
commit f5972c4d2c
2 changed files with 65 additions and 34 deletions

View File

@ -33,16 +33,19 @@
#endif #endif
FORCE_INLINE void store_char(unsigned char c) { FORCE_INLINE void store_char(unsigned char c) {
int i = (unsigned int)(rx_buffer.head + 1) % RX_BUFFER_SIZE; CRITICAL_SECTION_START;
uint8_t h = rx_buffer.head;
uint8_t i = (uint8_t)(h + 1) & (RX_BUFFER_SIZE - 1);
// if we should be storing the received character into the location // if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the // just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer // current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head. // and so we don't write the character or advance the head.
if (i != rx_buffer.tail) { if (i != rx_buffer.tail) {
rx_buffer.buffer[rx_buffer.head] = c; rx_buffer.buffer[h] = c;
rx_buffer.head = i; rx_buffer.head = i;
} }
CRITICAL_SECTION_END;
} }
@ -101,24 +104,32 @@ void MarlinSerial::end() {
int MarlinSerial::peek(void) { int MarlinSerial::peek(void) {
if (rx_buffer.head == rx_buffer.tail) { int v;
return -1; CRITICAL_SECTION_START;
uint8_t t = rx_buffer.tail;
if (rx_buffer.head == t) {
v = -1;
} }
else { else {
return rx_buffer.buffer[rx_buffer.tail]; v = rx_buffer.buffer[t];
} }
CRITICAL_SECTION_END;
return v;
} }
int MarlinSerial::read(void) { int MarlinSerial::read(void) {
// if the head isn't ahead of the tail, we don't have any characters int v;
if (rx_buffer.head == rx_buffer.tail) { CRITICAL_SECTION_START;
return -1; uint8_t t = rx_buffer.tail;
if (rx_buffer.head == t) {
v = -1;
} }
else { else {
unsigned char c = rx_buffer.buffer[rx_buffer.tail]; v = rx_buffer.buffer[t];
rx_buffer.tail = (unsigned int)(rx_buffer.tail + 1) % RX_BUFFER_SIZE; rx_buffer.tail = (uint8_t)(t + 1) & (RX_BUFFER_SIZE - 1);
return c;
} }
CRITICAL_SECTION_END;
return v;
} }
void MarlinSerial::flush() { void MarlinSerial::flush() {
@ -131,7 +142,9 @@ void MarlinSerial::flush() {
// the value to rx_buffer_tail; the previous value of rx_buffer_head // the value to rx_buffer_tail; the previous value of rx_buffer_head
// may be written to rx_buffer_tail, making it appear as if the buffer // may be written to rx_buffer_tail, making it appear as if the buffer
// were full, not empty. // were full, not empty.
rx_buffer.head = rx_buffer.tail; CRITICAL_SECTION_START;
rx_buffer.head = rx_buffer.tail;
CRITICAL_SECTION_END;
} }

View File

@ -23,6 +23,12 @@
#define MarlinSerial_h #define MarlinSerial_h
#include "Marlin.h" #include "Marlin.h"
#ifndef CRITICAL_SECTION_START
#define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli();
#define CRITICAL_SECTION_END SREG = _sreg;
#endif
#ifndef SERIAL_PORT #ifndef SERIAL_PORT
#define SERIAL_PORT 0 #define SERIAL_PORT 0
#endif #endif
@ -69,13 +75,18 @@
// using a ring buffer (I think), in which rx_buffer_head is the index of the // using a ring buffer (I think), in which rx_buffer_head is the index of the
// location to which to write the next incoming character and rx_buffer_tail // location to which to write the next incoming character and rx_buffer_tail
// is the index of the location from which to read. // is the index of the location from which to read.
#define RX_BUFFER_SIZE 128 // 256 is the max limit due to uint8_t head and tail. Use only powers of 2. (...,16,32,64,128,256)
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 128
#endif
#if !((RX_BUFFER_SIZE == 256) ||(RX_BUFFER_SIZE == 128) ||(RX_BUFFER_SIZE == 64) ||(RX_BUFFER_SIZE == 32) ||(RX_BUFFER_SIZE == 16) ||(RX_BUFFER_SIZE == 8) ||(RX_BUFFER_SIZE == 4) ||(RX_BUFFER_SIZE == 2))
#error RX_BUFFER_SIZE has to be a power of 2 and >= 2
#endif
struct ring_buffer { struct ring_buffer {
unsigned char buffer[RX_BUFFER_SIZE]; unsigned char buffer[RX_BUFFER_SIZE];
int head; volatile uint8_t head;
int tail; volatile uint8_t tail;
}; };
#if UART_PRESENT(SERIAL_PORT) #if UART_PRESENT(SERIAL_PORT)
@ -92,8 +103,12 @@ class MarlinSerial { //: public Stream
int read(void); int read(void);
void flush(void); void flush(void);
FORCE_INLINE int available(void) { FORCE_INLINE uint8_t available(void) {
return (unsigned int)(RX_BUFFER_SIZE + rx_buffer.head - rx_buffer.tail) % RX_BUFFER_SIZE; CRITICAL_SECTION_START;
uint8_t h = rx_buffer.head;
uint8_t t = rx_buffer.tail;
CRITICAL_SECTION_END;
return (uint8_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1);
} }
FORCE_INLINE void write(uint8_t c) { FORCE_INLINE void write(uint8_t c) {
@ -105,16 +120,19 @@ class MarlinSerial { //: public Stream
FORCE_INLINE void checkRx(void) { FORCE_INLINE void checkRx(void) {
if (TEST(M_UCSRxA, M_RXCx)) { if (TEST(M_UCSRxA, M_RXCx)) {
unsigned char c = M_UDRx; unsigned char c = M_UDRx;
int i = (unsigned int)(rx_buffer.head + 1) % RX_BUFFER_SIZE; CRITICAL_SECTION_START;
uint8_t h = rx_buffer.head;
uint8_t i = (uint8_t)(h + 1) & (RX_BUFFER_SIZE - 1);
// if we should be storing the received character into the location // if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the // just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer // current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head. // and so we don't write the character or advance the head.
if (i != rx_buffer.tail) { if (i != rx_buffer.tail) {
rx_buffer.buffer[rx_buffer.head] = c; rx_buffer.buffer[h] = c;
rx_buffer.head = i; rx_buffer.head = i;
} }
CRITICAL_SECTION_END;
} }
} }