/printbang

A modest serial transmitter for AVR MCUs in restricted circumstances

Primary LanguageCMIT LicenseMIT

A modest serial transmitter for AVR MCUs in restricted circumstances.

#define PRINTBANG_PORT PORTB
#define PRINTBANG_PIN PB0
#define PRINTBANG_IMPLEMENTATION
#include "printbang.h"
// ...
for (int i = 0;; i++)
{
    bangln(PSTR("Hello from program space!"));
    bang(PSTR("Running for ")); bang(i, 10); bangln(PSTR(" seconds."));
    _delay_ms(1000);
}

Why?

For AVR projects where you just don't have any more leeway than a digital pin, a few clock cycles, and a couple of flash bytes.

How?

printbang implements a cycle-counted transmission routine that is speed-limited by an user-defined assembly snippet. Instead of relying on a buffer like other software serial implementations, the function is called once for every word. This also minimizes the cycles spent in time-critical code.

Features

  • No dependency on timers or hardware UARTs/USI
  • Supports 1-8 data bits, LSB- or MSB-first transmission and even/odd parity
  • 250000 baud default configuration for common clock frequencies
  • Basic Arduino Serial-style formatting for numeric data types
  • Doesn't depend on the Arduino core or the C++ runtime
  • Doesn't depend on avr-libc formatting
  • State- and heapless

Caveats

  • Recursive formatting routines can result in heavy stack load for long integers
  • Interrupts are masked during transmission of a word

Documentation

Configuration macros

HAVE_PRINTBANG_CONFIG_H (source)

If this macro is defined, <printbang_config.h> will be included before printbang.h.

PRINTBANG_PORT and PRINTBANG_PORT_IO (source)

Either of these macros define the port of the pin used for serial output.

If PRINTBANG_PORT_IO is not defined, it will be derived from PRINTBANG_PORT by subtracting the SFR memory offset from it:

#define PRINTBANG_PORT PORTA
// is equivalent to
#define PRINTBANG_PORT_IO _SFR_IO_ADDR(PORTA)

PRINTBANG_PIN and PRINTBANG_PIN_MASK (source)

Either of these macros define the pin(s) on the chosen port to be used for serial output.

If PRINTBANG_PIN_MASK is not defined, it will be derived from PRINTBANG_PIN by converting it into a bit mask:

#define PRINTBANG_PIN PA0
// is equivalent to
#define PRINTBANG_PIN_MASK _BV(PA0)

PRINTBANG_DELAY (source)

This macro is an inline assembly snippet that limits the speed of the transmission routine to a particular baudrate. If it is not defined, the following defaults are used for common clock frequencies:

  • 16.5MHz: 250000 baud, 58 delay cycles, 0% deviation
  • 16MHz: 250000 baud, 56 delay cycles, 0% deviation
  • 8MHz: 250000 baud, 24 delay cycles, 0% deviation
  • 4MHz: 250000 baud, 8 delay cycles, 0% deviation

PRINTBANG_DELAY_CLOBBER (source)

This macro will be used as the clobber section of the inline assembly and allows delay snippets to clobber registers, e.g. for looping.

TODO: Use a temporary variable instead

PRINTBANG_PARITY_EVEN and PRINTBANG_PARITY_ODD (source)

If one of these macros is defined, a bit with the given parity will be appended to every transmitted word. This functionality depends on avr-libc's util/parity.h.

PRINTBANG_DATA_BITS (source)

This macro defines the number of data bits transmitted per word. Counting always starts at the least significant bit; if MSB-first transmission is used, the byte will be aligned to the left side.

// Saves one bit per word but limits transmission to ASCII characters
#define PRINTBANG_DATA_BITS 7

PRINTBANG_ORDER_MSB (source)

If this macro is defined, transmission will occur in MSB-first order. Otherwise, LSB-first order will be used.

PRINTBANG_LINE_ENDING (source)

This macro expands to a string literal that will be used by bangln to terminate a line. It defaults to "\r\n".

PRINTBANG_IMPLEMENTATION (source)

printbang is a header-only library. When including it, its functions are declared, but only defined if this macro is set.

The recommended way of using printbang in your project involves setting PRINTBANG_IMPLEMENTATION in a single source file, most commonly wherever your main function is located. All other source files can then include printbang.h without defining this macro.

Character and string transmission

void bang_char(char value) (source)

Transmits a single word over the serial pin. Interrupts are masked during the runtime of this function.

void bang_str(const char *str) (source)

Transmits a null-terminated string from RAM. Calling this function on a program-space string will result in garbage being transmitted.

void bang_pstr(PGM_P str) (source)

Transmits a null-terminated string from program space. Calling this function on a RAM string will result in garbage being transmitted.

Integer and floating point transmission

void bang_uint(unsigned int value, unsigned char base) (source)

void bang_int(int value, unsigned char base)

Transmits unsigned int respectively int values. The passed value is formatted in a given base.

void bang_ulong(unsigned long value, unsigned char base) (source)

void bang_long(long value, unsigned char base)

Transmits unsigned long respectively long values. The passed value is formatted in a given base.

void bang_ulonglong(unsigned long long value, unsigned char base (source)

void bang_longlong(long long value, unsigned char base)

Transmits unsigned long long respectively long long values. The passed value is formatted in a given base.

void bang_float(float value, unsigned char base) (source)

Transmits float values. The floating point formatting is very rudimentary and will simply concatenate the number to a given number of decimal places. One trailing zero is always appended.

Since double is an alias for float in avr-libc, this function should be used for double values as well.

void bang(...) (source)

bang provides a simple generic wrapper to all bang_x functions. If C++ is used, it is implemented as an overloaded wrapper function. If C is used, it is implemented as a _Generic macro.

Because gcc doesn't disinguish between const char * and const PROGMEM char * at compile-time, all constant strings are handled as program-space pointers by bang. If you want to bang a constant string literal from RAM you need to call bang_str directly, but consider wrapping it in PSTR(...) to put it in program space instead and save memory.

void bangln(...) (source)

This is a macro that first calls bang on the passed arguments and then bang_pstr on printbang_line_ending.