Is there a more detailed example about the UART_TRANSPORT implementation?

Greetings,
i have a little problem following along the documentation. I found this tutorial:

I understand, that I have to implement the following interface

void unittest_uart_begin();
void unittest_uart_putchar(char c);
void unittest_uart_flush();
void unittest_uart_end();

but I’m not quite sure, whether I understand everything correct, since I saw a few examples with something like printf(...) in the putchar function.
So I’m asking here just for clarification:
void unittest_uart_begin(): This function should initialize the UART system on my microcontroller, set the appropriate registers, defines the data bits and the stop bits etc right?

void unittest_uart_putchar(char c) is the transmit function I assume? So for transmitting one byte at a time from my controller to my IDE

but what is the flush and end function supposed to do? I hope somebody can clarify that. Thank you.

Correct. (Usually 8N1 format with the baud rate being the test_speed, default 115200).

Depending on the UART transmit implementation, the putchar() function may just push the data into a ringbuffer / circular buffer / FIFO that is then then (slowly) worked on byte by byte the UART interrupt. If that is the case, then when calling the unittest_uart_putchar function multiple times, at the end of the last call, it may not be guaranteed that all bytes have been physically sent out (they are waiting in the buffer). In that situation, we should be able to flush the queue of outstanding bytes out (to guarantee e.g. that after a particular line of code, the output is definitely already sent out). That is what flushing means. This is the usual implementation style in an Arduino core, because it does not slow the program down for sending via a (slow) UART baud rate, but just saves the data in a buffer. But, if the program crashes between saving it in the buffer and sending them all out, the UART output is lost. (But we can Serial.flush() against that as a synchronization barrier).

See also c++ - What exactly is flushing? - Stack Overflow.

There are of course other implementations for the putchar methods, namely a blocking implemenetation that does not return until the byte is physically sent out. This usually involves low-level waiting for the UART status register to indicitate “transmission buffer empty” (TBE), pushing the data into the UART data register, then waiting for the transmission to be completed by polling the TBE register again. In such cases, there is no buffer / queue to be flushed in the flush function, and the function can be empty.

Also remember that when you run on the e.g. Arduino framework, PlatformIO auto-implements these functions for you, by just mapping them to Serial.begin(), Serial.print(), Serial.flush() and Serial.end. For other frameworks (mbed, Zephyr, ESP-IDF, native/desktop), this is also pre-implemented for you.

You can see examples of a custom implementation, e.g. STM32Cube, in platformio-examples/unit-testing/stm32cube/test at develop · platformio/platformio-examples · GitHub. There, since the UART putchar function is implemented as HAL_UART_Transmit() which is blocking (with a max timeout), flushing is again a no-op.

The end function deinitializes the UART peripheral again (shutdown after the serial is done being used at the end of the UART test), e.g. by shutting down the clocks for it. This is usually not extremely criticial.

https://github.com/platformio/platformio-examples/blob/develop/unit-testing/stm32cube/test/unittest_transport.c#L84-L88

Well, and a short answer would have been to point you at the official Unity documentation: Unity/UnityConfigurationGuide.md at master · ThrowTheSwitch/Unity · GitHub

The functions (e.g. unittest_uart_putchar) in the custom implementation map back to these Unity functions (e.g., output_char) (here).

Thank you for your detailed answer. My problem is, that I implemented the init and transmit function as assumed. I can see the UART transmit the UNITY_END() signal. So I assume something is corrupted in the transmission so that the IDE does not recognise the signal?
I tried alot like rising an interrupt, explicitely setting the UART END bit, but still, the tests run infinitely.
I tried running a function, like this:

void test(void) {
    //blink LED one time
    TEST_PASS();
    //blink LED 10 times
}

as expected, after the first blink the LED does not blink anymore, still the test in my IDE does not terminate. Do you have maybe an idea whats the issue here?
I’m using an atmega168pa without any framework.

What is your full test c file? Can you test

#include <unity.h>

void test_always_ok(void) {
   TEST_ASSERT_EQUAL(1, 1);
}

int main() {
    UNITY_BEGIN();
    RUN_TEST(test_always_ok);
    UNITY_END(); // stop unit testing

    while(1){}
}

, does that also hang up at the end without any output?

i played around a little bit, each time UNITY_END() is called, my controller transmits a signal back to my Computer. So the following sends 10 signals to my computer (but the test does not terminate)

void test(void) {
    for(int i = 0; i < 10; ++i) {
        UNITY_END();
    }
}

This brings me to the assumption, that the signal is probably corrupted and therefore not recognised by platformio as end signal?!

your provided program sent one signal and still does not terminate:

The tests may start too fast before PlatformIO can open the serial monitor. Can you #include <util/delay.h> and _delay_ms(2000); at the start of main()?

Otherwise, after the testing has hung up again, abort the test console (Ctrl+C), make sure monitor_speed = 115200 is in the platformio.ini, open the serial monitor task and press the reset button. Is there any output at all?

I figured it out :smiley:

i set the test_speed variable to 115200 that was to high in order to have a low error transmit percentage (-3,5% as per the datasheet) and probably the UBRRn value was also wrong, which led to a corrupted transmission.
I now set test_speed = 19200 with a f_cpu rate of 16000000L which finally works :smiley:

one last question: is there a C variable for the test_speed too, so that I just have to define the values inside the platformio.ini and can use it everywhere (like for f_cpu)

By default not, but it would be a nice addition to the core to place that e.g. here. You can however fill it on yourself with a little bit of advanced scripting.

E.g., in a standard Uno environment

[env:uno]
platform = atmelavr
board = uno
framework = arduino
test_speed = 9600
extra_scripts = pre:inform_baud.py

with inform_baud.py as

Import("env")
test_speed = env.GetProjectOption("test_speed",default=115200)
env.Append(CPPDEFINES=[("PIO_TEST_BAUD", test_speed)])

The code (on both test/ and src/ actually) can use the macro PIO_TEST_BAUD.

When you e.g. do pio test --without-uploading --without-testing -v on the CLI you will see that the correct macro is passed in to all compiler invocations as -DPIO_TEST_BAUD=9600.