The Arduino Serial class consumes a lot of resources, and even the tiny cores serial class (TinyDebugSerial) adds overhead to the half duplex software UART code it seems to be based on. I decided to integrate my implementation of AVR305 with an Arduino sketch.
I started by making a directory called BasicSerial in the libraries directory. Inside I created a BasicSerial.S file for my assember code. In order for assembler code to be callable from C++, it is necessary to follow the avr-gcc register layout and calling convention, and mark the function name global. The TxByte function takes a single char as an argument, which gcc will put in r24. The Arduino core uses interrupts which would interfere with the software UART timing, so interrupts are disabled at the start of TxByte and re-enabled at the end. Here's the code:
#include <avr/io.h>
; correct for avr/io.h 0x20 port offset for io instructions
#define UART_Port (PORTB-0x20)
#define UART_Tx 0
#define bitcnt r18
#define delayArg 19
#if F_CPU == 8000000L
#warning Using 8Mhz CPU timing
#define TXDELAY 21
#elif F_CPU == 16000000L
#warning Using 16Mhz CPU timing
#define TXDELAY 44
#else
#error unrecognized F_CPU value
#endif
.global TxByte
; transmit byte in r24 - 15 instructions
; calling code must set Tx line to idle state (high) or 1st byte may be lost
; i.e. PORTB |= (1<<UART_Tx)
TxByte:
cli
sbi UART_Port-1, UART_Tx ; set Tx line to output
ldi bitcnt, 10 ; 1 start + 8 bit + 1 stop
com r24 ; invert and set carry
TxLoop:
; 10 cycle loop + delay
brcc tx1
cbi UART_Port, UART_Tx ; transmit a 0
tx1:
brcs TxDone
sbi UART_Port, UART_Tx ; transmit a 1
TxDone:
ldi delayArg, TXDELAY
TxDelay:
; delay (3 cycle * delayArg) -1
dec delayArg
brne TxDelay
lsr r24
dec bitcnt
brne TxLoop
reti ; return and enable interrupts
The last thing to do is to create a header file called BasicSerial.h:
extern "C" {
void TxByte(char);
}
If the extern "C" is left out, C++ name mangling will cause a mismatch. To use the code in the sketch, just include BasicSerial.h, and call TxByte as if it were a C function. Here's a sample sketch:
#include <BasicSerial.h>
// sketch to test Serial
// change LEDPIN based on your schematic
#define LEDPIN PINB1
void setup(){
DDRB |= (1<<LEDPIN); // set LED pin to output mode
}
void serOut(const char* str)
{
while (*str) TxByte (*str++);
}
void loop(){
serOut("Turning on LED\n");
PORTB |= (1<<LEDPIN); // turn on LED
delay(500); // 0.5 second delay
PORTB &= ~(1<<LEDPIN); // turn off LED
delay(1000); // 1 second delay
}
Download and run the sketch, open the Serial Monitor at 115,200bps, an you should see this:
I've posted BasicSerial.zip containing BasicSerial.S and BasicSerial.h. Have fun!
New year's update:
I've modified the code so the delay timing is calculated by a macro in BasicSerial.h. Just modify the line:
#define BAUD_RATE 115200
BasicSerialv2.zip
#define BAUD_RATE 115200
BasicSerialv2.zip