2 Star 1 Fork 1

976717326 / Teacup_Firmware

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
serial-avr.c 6.05 KB
一键复制 编辑 原始数据 按行查看 历史
/** \file
\brief Serial subsystem, AVR specific part.
To be included from serial.c, for details see there.
*/
#if defined TEACUP_C_INCLUDE && defined __AVR__
#include <avr/interrupt.h>
#include "memory_barrier.h"
#include "arduino.h"
#include "pinio.h"
/** \def BUFSIZE
Size of TX and RX buffers. MUST be a \f$2^n\f$ value.
Unlike ARM MCUs, which come with a hardware buffer, AVRs require a read and
transmit buffer implemented in software. This buffer not only raises
reliability, it also allows transmitting characters from interrupt context.
*/
#define BUFSIZE 64
/** \def ASCII_XOFF
ASCII XOFF character.
*/
#define ASCII_XOFF 19
/** \def ASCII_XON
ASCII XON character.
*/
#define ASCII_XON 17
#ifndef USB_SERIAL
/** RX buffer.
rxhead is the head pointer and points to the next available space.
rxtail is the tail pointer and points to last character in the buffer.
*/
volatile uint8_t rxhead = 0;
volatile uint8_t rxtail = 0;
volatile uint8_t rxbuf[BUFSIZE];
/** TX buffer.
Same mechanism as RX buffer.
*/
volatile uint8_t txhead = 0;
volatile uint8_t txtail = 0;
volatile uint8_t txbuf[BUFSIZE];
/** Ringbuffer logic.
head = written data pointer.
tail = read data pointer.
When head == tail, buffer is empty.
When head + 1 == tail, buffer is full.
Thus, number of available spaces in buffer is (tail - head) & bufsize.
Can write:
(tail - head - 1) & (BUFSIZE - 1)
Write to buffer:
buf[head++] = data; head &= (BUFSIZE - 1);
Can read:
(head - tail) & (BUFSIZE - 1)
Read from buffer:
data = buf[tail++]; tail &= (BUFSIZE - 1);
*/
/** \def buf_canread()
Check if we can read from this buffer.
*/
#define buf_canread(buffer) ((buffer ## head - buffer ## tail ) & \
(BUFSIZE - 1))
/** \def buf_pop()
Actually read from this buffer.
*/
#define buf_pop(buffer, data) do { \
data = buffer ## buf[buffer ## tail]; \
buffer ## tail = (buffer ## tail + 1) & \
(BUFSIZE - 1); \
} while (0)
/** \def buf_canwrite()
Check if we can write to this buffer.
*/
#define buf_canwrite(buffer) ((buffer ## tail - buffer ## head - 1) & \
(BUFSIZE - 1))
/** \def buf_push()
Actually write to this buffer.
*/
#define buf_push(buffer, data) do { \
buffer ## buf[buffer ## head] = data; \
buffer ## head = (buffer ## head + 1) & \
(BUFSIZE - 1); \
} while (0)
#ifdef XONXOFF
#define FLOWFLAG_STATE_XOFF 0
#define FLOWFLAG_SEND_XON 1
#define FLOWFLAG_SEND_XOFF 2
#define FLOWFLAG_STATE_XON 4
// initially, send an XON
volatile uint8_t flowflags = FLOWFLAG_SEND_XON;
#endif
/** Initialise serial subsystem.
Set up baud generator and interrupts, clear buffers.
*/
void serial_init() {
#if BAUD > 38401
UCSR0A = MASK(U2X0);
UBRR0 = (((F_CPU / 8) / BAUD) - 0.5);
#else
UCSR0A = 0;
UBRR0 = (((F_CPU / 16) / BAUD) - 0.5);
#endif
UCSR0B = MASK(RXEN0) | MASK(TXEN0);
UCSR0C = MASK(UCSZ01) | MASK(UCSZ00);
UCSR0B |= MASK(RXCIE0) | MASK(UDRIE0);
}
/** Receive interrupt.
We have received a character, stuff it in the RX buffer if we can, or drop
it if we can't. Using the pragma inside the function is incompatible with
Arduinos' gcc.
*/
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#ifdef USART_RX_vect
ISR(USART_RX_vect)
#else
ISR(USART0_RX_vect)
#endif
{
if (buf_canwrite(rx))
buf_push(rx, UDR0);
else {
// Not reading the character makes the interrupt logic to swamp us with
// retries, so better read it and throw it away.
//#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
uint8_t trash;
//#pragma GCC diagnostic pop
trash = UDR0;
}
#ifdef XONXOFF
if (flowflags & FLOWFLAG_STATE_XON && buf_canwrite(rx) <= 16) {
// The buffer has only 16 free characters left, so send an XOFF.
// More characters might come in until the XOFF takes effect.
flowflags = FLOWFLAG_SEND_XOFF | FLOWFLAG_STATE_XON;
// Enable TX interrupt so we can send this character.
UCSR0B |= MASK(UDRIE0);
}
#endif
}
#pragma GCC diagnostic pop
/** Transmit buffer ready interrupt.
Provide the next character to transmit if we can, otherwise disable this
interrupt.
*/
#ifdef USART_UDRE_vect
ISR(USART_UDRE_vect)
#else
ISR(USART0_UDRE_vect)
#endif
{
#ifdef XONXOFF
if (flowflags & FLOWFLAG_SEND_XON) {
UDR0 = ASCII_XON;
flowflags = FLOWFLAG_STATE_XON;
}
else if (flowflags & FLOWFLAG_SEND_XOFF) {
UDR0 = ASCII_XOFF;
flowflags = FLOWFLAG_STATE_XOFF;
}
else
#endif
if (buf_canread(tx))
buf_pop(tx, UDR0);
else
UCSR0B &= ~MASK(UDRIE0);
}
/** Check how many characters can be read.
*/
uint8_t serial_rxchars() {
return buf_canread(rx);
}
/** Read one character.
*/
uint8_t serial_popchar() {
uint8_t c = 0;
// It's imperative that we check, because if the buffer is empty and we
// pop, we'll go through the whole buffer again.
if (buf_canread(rx))
buf_pop(rx, c);
#ifdef XONXOFF
if ((flowflags & FLOWFLAG_STATE_XON) == 0 && buf_canread(rx) <= 16) {
// The buffer has (BUFSIZE - 16) free characters again, so send an XON.
flowflags = FLOWFLAG_SEND_XON;
UCSR0B |= MASK(UDRIE0);
}
#endif
return c;
}
/** Send one character.
*/
void serial_writechar(uint8_t data) {
// Check if interrupts are enabled.
if (SREG & MASK(SREG_I)) {
// If they are, we should be ok to block since the tx buffer is emptied
// from an interrupt.
for ( ; buf_canwrite(tx) == 0; ) ;
buf_push(tx, data);
}
else {
// Interrupts are disabled -- maybe we're in one?
// Anyway, instead of blocking, only write if we have room.
if (buf_canwrite(tx))
buf_push(tx, data);
}
// Enable TX interrupt so we can send this character.
UCSR0B |= MASK(UDRIE0);
}
#endif /* USB_SERIAL */
#endif /* defined TEACUP_C_INCLUDE && defined __AVR__ */
C
1
https://gitee.com/rhh/Teacup_Firmware.git
git@gitee.com:rhh/Teacup_Firmware.git
rhh
Teacup_Firmware
Teacup_Firmware
master

搜索帮助