2016-05-25

Adding Print Macros

In the last article, I added the capability to write debug output via UART. Since I hooked this up to Rust's formatting infrastructure, this gives us the capability to write all kinds of complex output.

To do that, however, we need to pass the Uart struct around to all functions that need to print output. This makes it very inconvenient to add and remove debug output on the fly. In addition, we can't use this infrastructure from interrupt handlers, as we can't pass arguments into those.

What we need to solve those problems is a global instance of the Uart struct that we can call from everywhere, and a convenient way to access it. After thinking about this problem for a bit, I decided it was best to mimic the print! macro from Rust's standard libary.

Here's that global variable:

pub static mut UART: Option<Uart> = None;
            

It starts out in an uninitialized state. We need to run the initialization code before we can use it.

pub unsafe fn init(pin: uart::UndefinedPin) {
    UART = Some(Uart::new(pin));
}
            

After initialization, we can use the following macro to print debug output:

macro_rules! print {
    ($($args:tt)*) => {
        // Ignore logging errors. It's not worth killing the program because of
        // failed debug output. It would be nicer to save the error and report
        // it later, however.
        if let &mut Some(ref mut uart) = unsafe { &mut debug::UART } {
            let _ = write!(uart, $($args)*);
        }
    }
}
            

The macro checks, if the global variable has been initialized. If it has been, it uses it to print the debug output.

As a matter of convenience, I've also added a println! macro:[1]

macro_rules! println {
    ($fmt:expr)               => ( print!(concat!($fmt, '\n')) );
    ($fmt:expr, $($args:tt)*) => ( print!(concat!($fmt, '\n'), $($args)*) );
}
            

Here's our updated program:

pub fn start() {
    // LED blinking initialization code omitted

    let uart_tx = unsafe { pio::a().pin_9() };
    unsafe { debug::init(uart_tx) };

    loop {
        println!("Start main loop iteration");

        // LED blinking code and other details omitted
    }
}

            

And that's it. Now we can conveniently insert print! and println! calls whereever we want, without needing to update the surrounding code.

This wraps us up for today! As always, the full code is available on GitHub. See you next time!