132 lines
5.8 KiB
C++
132 lines
5.8 KiB
C++
/* UART0.cpp - Hardware serial library for USART0
|
|
* This library is free software released under LGPL 2.1.
|
|
* See License.md for more information.
|
|
* This file is part of megaTinyCore.
|
|
*
|
|
* Copyright (c) 2006 Nicholas Zambetti, Modified by
|
|
* 11/23/2006 David A. Mellis, 9/20/2010 Mark Sproul,
|
|
* 8/24/2012 Alarus, 12/3/2013 Matthijs Kooijman
|
|
* unknown others 2013-2020, 2020-2022 Spence Konde
|
|
*/
|
|
|
|
/* Each HardwareSerial is defined in its own file, sine the linker pulls
|
|
* in the entire file when any element inside is used. --gc-sections can
|
|
* additionally cause unused symbols to be dropped, but ISRs have the
|
|
* "used" attribute so are never dropped and they keep the
|
|
* HardwareSerial instance in as well. Putting each instance in its own
|
|
* file prevents the linker from pulling in any unused instances in the
|
|
* first place.
|
|
* There are now two versions of each ISR. All versions are stubs to call
|
|
* the real code (so it is not duplicated). The ASM versions are more
|
|
* efficient than calling normally can ever be because they completely
|
|
* ignore the ABI rules for the transition from duplicated to shared
|
|
* code and call it the most effcieient way possiblem so the almost all of
|
|
* the flash overhead is in the shared section. They'll stop working if
|
|
* anything changes, and it's not entirely clear whether the trick of
|
|
* dropping out of assembly while naked to grab the address is legal,
|
|
* though there's no reason it shouldn't be.
|
|
* The assembly implementations over in UART.cpp depends on the structure
|
|
* of the SerialClass. Any changes to the class member variables will
|
|
* require changes to the asm to match.
|
|
* This was done for 1.5.12 to correct a serious defect. amd fix a
|
|
* to make things work better with millis off, and free up 4 bytes of sram.
|
|
*/
|
|
#include "Arduino.h"
|
|
#include "UART.h"
|
|
#include "UART_private.h"
|
|
|
|
#if defined(USART0)
|
|
#if defined(USE_ASM_TXC) && USE_ASM_TXC == 1 //&& defined(USART1) // No benefit to this if it's just one USART
|
|
// Note the difference between this and the other ISRs - here we don't care at all about the serial object, we just have to work with the USART
|
|
ISR(USART0_TXC_vect, ISR_NAKED) {
|
|
__asm__ __volatile__(
|
|
"push r30" "\n\t" // push the low byte of Z - we start out 5-6 clocks behind the ball, these three instructions take 4-5 -> 9 or 11 by th time we reach _do_txc
|
|
"ldi r30, 0x00" "\n\t" // and put the low bit of this USART there - 0x20 * n
|
|
#if PROGMEM_SIZE > 8192
|
|
"jmp _do_txc" "\n\t"
|
|
#else
|
|
"rjmp _do_txc" "\n\t"
|
|
#endif // _do_txc pushes the other necessary registers and loads 0x08 into the high byte.
|
|
// The reason this is possible here and not elsewhere is because TXC only needs the USART, while the others need the HardwareSerial instance.
|
|
:::);
|
|
}
|
|
#elif defined(USE_ASM_TXC) && USE_ASM_TXC == 2
|
|
ISR(USART0_TXC_vect, ISR_NAKED) {
|
|
__asm__ __volatile__(
|
|
"push r30" "\n\t"
|
|
"push r31" "\n\t"
|
|
:::);
|
|
__asm__ __volatile__(
|
|
#if PROGMEM_SIZE > 8192
|
|
"jmp _do_txc" "\n\t"
|
|
#else
|
|
"rjmp _do_txc" "\n\t"
|
|
#endif
|
|
::"z"(&Serial0));
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
#else
|
|
ISR(USART0_TXC_vect) {
|
|
// only enabled in half duplex mode - we disable RX interrupt while sending.
|
|
// When we are done sending, we re-enable the RX interrupt and disable this one.
|
|
// Note that we do NOT clear TXC flag, which the flush() method relies on.
|
|
uint8_t ctrla;
|
|
while (USART0.STATUS & USART_RXCIF_bm) {
|
|
ctrla = USART0.RXDATAL; // We sent these, so dump them, using local var as trashcan.
|
|
}
|
|
ctrla = USART0.CTRLA; // Get current CTRLA
|
|
ctrla |= USART_RXCIE_bm; // turn on receive complete
|
|
ctrla &= ~USART_TXCIE_bm; // turn off transmit complete
|
|
USART0.CTRLA = ctrla; // Write it back to CTRLA.
|
|
}
|
|
#endif
|
|
|
|
#if !(defined(USE_ASM_RXC) && (USE_ASM_RXC == 1 || USE_ASM_RXC == 2) && (SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) /* && defined(USART1)*/)
|
|
ISR(USART0_RXC_vect) {
|
|
HardwareSerial::_rx_complete_irq(Serial);
|
|
}
|
|
#else
|
|
ISR(USART0_RXC_vect, ISR_NAKED) {
|
|
__asm__ __volatile__(
|
|
"push r30" "\n\t" //we start out 5-6 clocks behind the ball, then do 2 push + 2 ldi + 2-3 for jump = 11 or 13 clocks to _do_rxc (and dre is the same)
|
|
"push r31" "\n\t"
|
|
"cbi 0x1F, 0" "\n\t"
|
|
:::);
|
|
__asm__ __volatile__(
|
|
#if PROGMEM_SIZE > 8192
|
|
"jmp _do_rxc" "\n\t"
|
|
#else
|
|
"rjmp _do_rxc" "\n\t"
|
|
#endif
|
|
::"z"(&Serial0));
|
|
__builtin_unreachable();
|
|
}
|
|
#endif
|
|
#if !(defined(USE_ASM_DRE) && USE_ASM_DRE == 1 && \
|
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_RX_BUFFER_SIZE == 128 || SERIAL_RX_BUFFER_SIZE == 64 || SERIAL_RX_BUFFER_SIZE == 32 || SERIAL_RX_BUFFER_SIZE == 16) && \
|
|
(SERIAL_RX_BUFFER_SIZE == 256 || SERIAL_TX_BUFFER_SIZE == 128 || SERIAL_TX_BUFFER_SIZE == 64 || SERIAL_TX_BUFFER_SIZE == 32 || SERIAL_TX_BUFFER_SIZE == 16))
|
|
ISR(USART0_DRE_vect) {
|
|
HardwareSerial::_tx_data_empty_irq(Serial);
|
|
}
|
|
#else
|
|
ISR(USART0_DRE_vect, ISR_NAKED) {
|
|
__asm__ __volatile__(
|
|
"push r30" "\n\t"
|
|
"push r31" "\n\t"
|
|
"cbi 0x1F, 0" "\n\t"
|
|
:::);
|
|
__asm__ __volatile__(
|
|
#if PROGMEM_SIZE > 8192
|
|
"jmp _do_dre" "\n\t"
|
|
#else
|
|
"rjmp _do_dre" "\n\t"
|
|
#endif
|
|
::"z"(&Serial0));
|
|
__builtin_unreachable();
|
|
}
|
|
#endif
|
|
|
|
HardwareSerial Serial0(&USART0, 0, HWSERIAL0_MUX_DEFAULT); //Elsewhere we define an alias of Serial for this :-)
|
|
#endif
|