Skip to content

9-bit frame support for HardwareSerial 1.5.X #2291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 76 additions & 16 deletions hardware/arduino/avr/cores/arduino/HardwareSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,25 @@ void HardwareSerial::_tx_udr_empty_irq(void)
{
// If interrupts are enabled, there must be more data in the output
// buffer. Send the next byte
unsigned char c = _tx_buffer[_tx_buffer_tail];
_tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_TX_BUFFER_SIZE;

if(bit_is_set(*_ucsrb, UCSZ02)) {
// If Uart is configured for 9 bit mode
unsigned char mb = _tx_buffer[_tx_buffer_tail];
unsigned char c = _tx_buffer[_tx_buffer_tail + 1];
_tx_buffer_tail = (_tx_buffer_tail + 2) % SERIAL_TX_BUFFER_SIZE;
if(mb & 0x01) {
sbi(*_ucsrb, TXB80);
} else {
cbi(*_ucsrb, TXB80);
}
*_udr = c;
} else {
// UART is configured for 5 to 8 bit modes
unsigned char c = _tx_buffer[_tx_buffer_tail];
_tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_TX_BUFFER_SIZE;

*_udr = c;
*_udr = c;
}

// clear the TXC bit -- "can be cleared by writing a one to its bit
// location". This makes sure flush() won't return until the bytes
Expand All @@ -100,7 +115,7 @@ void HardwareSerial::_tx_udr_empty_irq(void)

// Public Methods //////////////////////////////////////////////////////////////

void HardwareSerial::begin(unsigned long baud, byte config)
void HardwareSerial::begin(unsigned long baud, uint16_t config)
{
// Try u2x mode first
uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
Expand All @@ -127,8 +142,12 @@ void HardwareSerial::begin(unsigned long baud, byte config)
#if defined(__AVR_ATmega8__)
config |= 0x80; // select UCSRC register (shared with UBRRH)
#endif
*_ucsrc = config;


if(config & 0x100) {
sbi(*_ucsrb, UCSZ02);
}
*_ucsrc = (uint8_t) config;

sbi(*_ucsrb, RXEN0);
sbi(*_ucsrb, TXEN0);
sbi(*_ucsrb, RXCIE0);
Expand All @@ -152,15 +171,28 @@ void HardwareSerial::end()

int HardwareSerial::available(void)
{
return (int)(SERIAL_RX_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail) % SERIAL_RX_BUFFER_SIZE;
unsigned int a = (unsigned int) (SERIAL_RX_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail) % SERIAL_RX_BUFFER_SIZE;
if(bit_is_set(*_ucsrb, UCSZ02)) {
// If Uart is in 9 bit mode return only the half, because we use two bytes per 9 bit "byte".
return a / 2;
}
else {
// For 5 - 8 bit modes simply return the number
return a;
}
}

int HardwareSerial::peek(void)
{
if (_rx_buffer_head == _rx_buffer_tail) {
return -1;
} else {
return _rx_buffer[_rx_buffer_tail];
if(bit_is_set(*_ucsrb, UCSZ02)) {
// If Uart is in 9 bit mode read two bytes and merge them
return (_rx_buffer[_rx_buffer_tail] << 8) | _rx_buffer[_rx_buffer_tail + 1 % SERIAL_RX_BUFFER_SIZE];
} else {
return _rx_buffer[_rx_buffer_tail];
}
}
}

Expand All @@ -170,9 +202,17 @@ int HardwareSerial::read(void)
if (_rx_buffer_head == _rx_buffer_tail) {
return -1;
} else {
unsigned char c = _rx_buffer[_rx_buffer_tail];
_rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE;
return c;
if(bit_is_set(*_ucsrb, UCSZ02)) {
// If Uart is in 9 bit mode read two bytes and merge them
unsigned char mb = _rx_buffer[_rx_buffer_tail];
unsigned char c = _rx_buffer[_rx_buffer_tail + 1];
_rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 2) % SERIAL_RX_BUFFER_SIZE;
return ((mb << 8) | c);
} else {
unsigned char c = _rx_buffer[_rx_buffer_tail];
_rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE;
return c;
}
}
}

Expand Down Expand Up @@ -211,19 +251,33 @@ void HardwareSerial::flush()
// the hardware finished tranmission (TXC is set).
}

size_t HardwareSerial::write(uint8_t c)
size_t HardwareSerial::write(uint16_t c)
{
// If the buffer and the data register is empty, just write the byte
// to the data register and be done. This shortcut helps
// significantly improve the effective datarate at high (>
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown.
if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) {
*_udr = c;
if(bit_is_set(*_ucsrb, UCSZ02)) {
// in 9 bit mode set TXB8 bit if necessary
if(c & 0x100) {
sbi(*_ucsrb, TXB80);
} else {
cbi(*_ucsrb, TXB80);
}
}
*_udr = (uint8_t) c;
sbi(*_ucsra, TXC0);
return 1;
}
tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;


tx_buffer_index_t i;

if(bit_is_set(*_ucsrb, UCSZ02)) {
i = ((_tx_buffer_head + 2) % SERIAL_TX_BUFFER_SIZE);
} else {
i = ((_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE);
}
// If the output buffer is full, there's nothing for it other than to
// wait for the interrupt handler to empty it a bit
while (i == _tx_buffer_tail) {
Expand All @@ -239,7 +293,13 @@ size_t HardwareSerial::write(uint8_t c)
}
}

_tx_buffer[_tx_buffer_head] = c;

if(bit_is_set(*_ucsrb, UCSZ02)) {
_tx_buffer[_tx_buffer_head] = (uint8_t) (c >> 8) & 0x01;
_tx_buffer[_tx_buffer_head + 1] = (uint8_t) c;
} else {
_tx_buffer[_tx_buffer_head] = (uint8_t) c;
}
_tx_buffer_head = i;

sbi(*_ucsrb, UDRIE0);
Expand Down
72 changes: 42 additions & 30 deletions hardware/arduino/avr/cores/arduino/HardwareSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,30 +53,41 @@ typedef uint8_t rx_buffer_index_t;
#endif

// Define config for Serial.begin(baud, config);
#define SERIAL_5N1 0x00
#define SERIAL_6N1 0x02
#define SERIAL_7N1 0x04
#define SERIAL_8N1 0x06
#define SERIAL_5N2 0x08
#define SERIAL_6N2 0x0A
#define SERIAL_7N2 0x0C
#define SERIAL_8N2 0x0E
#define SERIAL_5E1 0x20
#define SERIAL_6E1 0x22
#define SERIAL_7E1 0x24
#define SERIAL_8E1 0x26
#define SERIAL_5E2 0x28
#define SERIAL_6E2 0x2A
#define SERIAL_7E2 0x2C
#define SERIAL_8E2 0x2E
#define SERIAL_5O1 0x30
#define SERIAL_6O1 0x32
#define SERIAL_7O1 0x34
#define SERIAL_8O1 0x36
#define SERIAL_5O2 0x38
#define SERIAL_6O2 0x3A
#define SERIAL_7O2 0x3C
#define SERIAL_8O2 0x3E
#define SERIAL_5N1 0x000 //0b000000000
#define SERIAL_6N1 0x002 //0b000000010
#define SERIAL_7N1 0x004 //0b000000100
#define SERIAL_8N1 0x006 //0b000000110
#define SERIAL_9N1 0x106 //0b100000110

#define SERIAL_5N2 0x008 //0b000001000
#define SERIAL_6N2 0x00A //0b000001010
#define SERIAL_7N2 0x00C //0b000001100
#define SERIAL_8N2 0x00E //0b000001110
#define SERIAL_9N2 0x10E //0b100001110

#define SERIAL_5E1 0x020 //0b000100000
#define SERIAL_6E1 0x022 //0b000100010
#define SERIAL_7E1 0x024 //0b000100100
#define SERIAL_8E1 0x026 //0b000100110
#define SERIAL_9E1 0x126 //0b100100110

#define SERIAL_5E2 0x028 //0b000101000
#define SERIAL_6E2 0x02A //0b000101010
#define SERIAL_7E2 0x02C //0b000101100
#define SERIAL_8E2 0x02E //0b000101110
#define SERIAL_9E2 0x12E //0b100101110

#define SERIAL_5O1 0x030 //0b000110000
#define SERIAL_6O1 0x032 //0b000110010
#define SERIAL_7O1 0x034 //0b000110100
#define SERIAL_8O1 0x036 //0b000110110
#define SERIAL_9O1 0x136 //0b100110110

#define SERIAL_5O2 0x038 //0b000111000
#define SERIAL_6O2 0x03A //0b000111010
#define SERIAL_7O2 0x03C //0b000111100
#define SERIAL_8O2 0x03E //0b000111110
#define SERIAL_9O2 0x13E //0b100111110

class HardwareSerial : public Stream
{
Expand Down Expand Up @@ -107,18 +118,19 @@ class HardwareSerial : public Stream
volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
volatile uint8_t *ucsrc, volatile uint8_t *udr);
void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
void begin(unsigned long, uint8_t);
void begin(unsigned long, uint16_t);
void end();
virtual int available(void);
virtual int peek(void);
virtual int read(void);
int availableForWrite(void);
virtual void flush(void);
virtual size_t write(uint8_t);
inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
virtual size_t write(uint16_t);
inline size_t write(unsigned long n) { return write((uint16_t)n); }
inline size_t write(long n) { return write((uint16_t)n); }
inline size_t write(int n) { return write((uint16_t)n); }
inline size_t write(int8_t n) { return write((uint16_t)n); }
inline size_t write(uint8_t n) { return write((uint16_t)n); }
using Print::write; // pull in write(str) and write(buf, size) from Print
operator bool() { return true; }

Expand Down
37 changes: 31 additions & 6 deletions hardware/arduino/avr/cores/arduino/HardwareSerial_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
#define U2X0 U2X
#define UPE0 UPE
#define UDRE0 UDRE
#define UCSZ02 UCSZ2
#define TXB80 TXB8
#define RXB80 RXB8
#elif defined(TXC1)
// Some devices have uart1 but no uart0
#define TXC0 TXC1
Expand All @@ -58,6 +61,9 @@
#define U2X0 U2X1
#define UPE0 UPE1
#define UDRE0 UDRE1
#define UCSZ02 UCSZ12
#define TXB80 TXB81
#define RXB80 RXB81
#else
#error No UART found in HardwareSerial.cpp
#endif
Expand All @@ -68,17 +74,17 @@
// changed for future hardware.
#if defined(TXC1) && (TXC1 != TXC0 || RXEN1 != RXEN0 || RXCIE1 != RXCIE0 || \
UDRIE1 != UDRIE0 || U2X1 != U2X0 || UPE1 != UPE0 || \
UDRE1 != UDRE0)
UDRE1 != UDRE0 || UCSZ12 != UCSZ02 || TXB81 != TXB80 || RXB81 != RXB80)
#error "Not all bit positions for UART1 are the same as for UART0"
#endif
#if defined(TXC2) && (TXC2 != TXC0 || RXEN2 != RXEN0 || RXCIE2 != RXCIE0 || \
UDRIE2 != UDRIE0 || U2X2 != U2X0 || UPE2 != UPE0 || \
UDRE2 != UDRE0)
UDRE2 != UDRE0 || UCSZ22 != UCSZ02 || TXB82 != TXB80 || RXB82 != RXB80)
#error "Not all bit positions for UART2 are the same as for UART0"
#endif
#if defined(TXC3) && (TXC3 != TXC0 || RXEN3 != RXEN0 || RXCIE3 != RXCIE0 || \
UDRIE3 != UDRIE0 || U3X3 != U3X0 || UPE3 != UPE0 || \
UDRE3 != UDRE0)
UDRE3 != UDRE0 || UCSZ32 != UCSZ02 || TXB83 != TXB80 || TXB83 != TXB80)
#error "Not all bit positions for UART3 are the same as for UART0"
#endif

Expand All @@ -103,15 +109,34 @@ void HardwareSerial::_rx_complete_irq(void)
if (bit_is_clear(*_ucsra, UPE0)) {
// No Parity error, read byte and store it in the buffer if there is
// room
unsigned char c = *_udr;
rx_buffer_index_t i = (unsigned int)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE;
rx_buffer_index_t i;
unsigned char mb;
unsigned char c;

if(bit_is_set(*_ucsrb, UCSZ02)) {
// If Uart is configured for 9 bit mode
i = (unsigned int)(_rx_buffer_head + 2) % SERIAL_RX_BUFFER_SIZE;
mb = (*_ucsrb >> RXB80) & 0x01;
c = *_udr;
} else {
// UART is configured for 5 to 8 bit modes
i = (unsigned int)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE;
c = *_udr;
}

// if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head.
if (i != _rx_buffer_tail) {
_rx_buffer[_rx_buffer_head] = c;
if(bit_is_set(*_ucsrb, UCSZ02)) {
// If Uart is configured for 9 bit mode
_rx_buffer[_rx_buffer_head] = mb;
_rx_buffer[_rx_buffer_head + 1] = c;
} else {
// UART is configured for 5 to 8 bit modes
_rx_buffer[_rx_buffer_head] = c;
}
_rx_buffer_head = i;
}
} else {
Expand Down
41 changes: 41 additions & 0 deletions hardware/arduino/sam/cores/arduino/HardwareSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,47 @@

#include "Stream.h"

// Define config for Serial.begin(baud, config);
#define SERIAL_5N1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_5_BIT | US_MR_PAR_NO | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_6N1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_6_BIT | US_MR_PAR_NO | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_7N1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_7_BIT | US_MR_PAR_NO | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_8N1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_8_BIT | US_MR_PAR_NO | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_9N1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_MODE9 | US_MR_PAR_NO | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)

#define SERIAL_5N2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_5_BIT | US_MR_PAR_NO | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_6N2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_6_BIT | US_MR_PAR_NO | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_7N2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_7_BIT | US_MR_PAR_NO | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_8N2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_8_BIT | US_MR_PAR_NO | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_9N2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_MODE9 | US_MR_PAR_NO | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)

#define SERIAL_5E1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_5_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_6E1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_6_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_7E1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_7_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_8E1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_8_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_9E1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_MODE9 | US_MR_PAR_EVEN | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)


#define SERIAL_5E2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_5_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_6E2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_6_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_7E2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_7_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_8E2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_8_BIT | US_MR_PAR_EVEN | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_9E2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_MODE9 | US_MR_PAR_EVEN | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)


#define SERIAL_5O1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_5_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_6O1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_6_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_7O1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_7_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_8O1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_8_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_9O1 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_MODE9 | US_MR_PAR_ODD | US_MR_NBSTOP_1_BIT | US_MR_CHMODE_NORMAL)


#define SERIAL_5O2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_5_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_6O2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_6_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_7O2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_7_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_8O2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_8_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)
#define SERIAL_9O2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_MODE9 | US_MR_PAR_ODD | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL)


class HardwareSerial : public Stream
{
public:
Expand Down
2 changes: 1 addition & 1 deletion hardware/arduino/sam/cores/arduino/RingBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ RingBuffer::RingBuffer( void )
_iTail=0 ;
}

void RingBuffer::store_char( uint8_t c )
void RingBuffer::store_char( uint16_t c )
{
int i = (uint32_t)(_iHead + 1) % SERIAL_BUFFER_SIZE ;

Expand Down
Loading