Help using ATtiny402 USART with printf

I am trying to follow Microchip application note TB3216

I am successful with the ‘Hello World’ example (Chapter3) of printing a string but am struggling with the use of the printf (Chapter 4). I just want to have some simple debug output to hand. I have ‘mashed’ a print integer routine together but it seems inelegant and using printf seems to be a better way.

The example tried to demonstrate changing the output stream of the printf function but I get stuck on the FDEV_SETUP_STREAM definition.

I have tried moving the location of this definition around with no success.

Please see source code, platformio.ini and output from compiler. Do you have any suggestions?

// uses demo code from Microchip TB3216 <https://ww1.microchip.com/downloads/en/Appnotes/TB3216-Getting-Started-with-USART-DS90003216.pdf>

#include <Arduino.h>
#define F_CPU 20000000
#define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)

#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdio.h>

//FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);

void USART0_init(void);
void USART0_sendChar(char c);
void USART0_sendString(char *str);
int USART0_printChar(char character, FILE *stream);

FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);

void setup() {
  // put your setup code here, to run once://
  USART0_init();
  stdout = &USART_stream;
  USART0_sendString("\r\nStarting\r\n");
}

void loop() {
  // put your main code here, to run repeatedly:
  //USART0_sendString("Basic\r\n");
  static int val = 10;
  printf("Counter value is: %d\r\n", val--);
  _delay_ms(500);
}

void USART0_init(void){
  /*  pin information
			     ATtiny402
		  	     ----u---
  		    VDD|		 |GND
	TXD BUZ PA6|		 |PA3 SIG
 RXD SW_M PA7|		 |PA0 UDIP
	  	PWR PA1|		 |PA2 SW_P
  		    	 --------
  */
  PORTA.DIR &= ~PIN7_bm;  // input
  PORTA.DIR |= PIN6_bm;   // output
  USART0.BAUD = (uint16_t)USART0_BAUD_RATE(115200);
  USART0.CTRLB |= USART_TXEN_bm;
}

void USART0_sendChar(char c){
  while (!(USART0.STATUS & USART_DREIF_bm));
  USART0.TXDATAL = c;
}

void USART0_sendString(char *str){
  for(size_t i = 0; i < strlen(str); i++){
  USART0_sendChar(str[i]);
  }
}

int USART0_printChar(char character, FILE *stream){
  while (!(USART0.STATUS & USART_DREIF_bm));
  return 0;
}
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:ATtiny402]
platform = atmelmegaavr
board = ATtiny402
framework = arduino
board_build.f_cpu = 20000000L
upload_speed = 115200
upload_port = COM7
upload_flags =
    --tool
    uart
    --device
    attiny402
    --uart
    $UPLOAD_PORT
    --clk
    $UPLOAD_SPEED
upload_command = pymcuprog write --erase $UPLOAD_FLAGS --filename $SOURCE
Processing ATtiny402 (platform: atmelmegaavr; board: ATtiny402; framework: arduino)
------------------------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelmegaavr/ATtiny402.html
PLATFORM: Atmel megaAVR (1.6.0) > ATtiny402
HARDWARE: ATTINY402 20MHz, 256B RAM, 4KB Flash
PACKAGES:
 - framework-arduino-megaavr-megatinycore @ 2.5.11
 - toolchain-atmelavr @ 3.70300.220127 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 15 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Compiling .pio\build\ATtiny402\src\main.cpp.o
Compiling .pio\build\ATtiny402\FrameworkArduino\WInterrupts_PC.c.o
Compiling .pio\build\ATtiny402\FrameworkArduino\WMath.cpp.o
Compiling .pio\build\ATtiny402\FrameworkArduino\abi.cpp.o
Compiling .pio\build\ATtiny402\FrameworkArduino\api\Common.cpp.o
Compiling .pio\build\ATtiny402\FrameworkArduino\api\IPAddress.cpp.o
Compiling .pio\build\ATtiny402\FrameworkArduino\api\PluggableUSB.cpp.o
Compiling .pio\build\ATtiny402\FrameworkArduino\api\Print.cpp.o
In file included from C:\Users\Peter\.platformio\packages\framework-arduino-megaavr-megatinycore\cores\megatinycore/api/Print.h:22:0,
                 from C:\Users\Peter\.platformio\packages\framework-arduino-megaavr-megatinycore\cores\megatinycore/api/Stream.h:25,
                 from C:\Users\Peter\.platformio\packages\framework-arduino-megaavr-megatinycore\cores\megatinycore/api/Client.h:22,
                 from C:\Users\Peter\.platformio\packages\framework-arduino-megaavr-megatinycore\cores\megatinycore/api/ArduinoAPI.h:29,
                 from C:\Users\Peter\.platformio\packages\framework-arduino-megaavr-megatinycore\cores\megatinycore/Arduino.h:23,
                 from src\main.cpp:4:
src\main.cpp:21:21: sorry, unimplemented: non-trivial designated initializers not supported
 FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);
                     ^
src\main.cpp:21:21: sorry, unimplemented: non-trivial designated initializers not supported
src\main.cpp:21:21: sorry, unimplemented: non-trivial designated initializers not supported
*** [.pio\build\ATtiny402\src\main.cpp.o] Error 1
================================================= [FAILED] Took 7.23 seconds =================================================
 *  The terminal process "C:\Users\Peter\.platformio\penv\Scripts\platformio.exe 'run', '--environment', 'ATtiny402'" terminated with exit code: 1. 
 *  Terminal will be reused by tasks, press any key to close it. 

I tried uploading the code direct from GITHUB without the arduino framework (see code below) - same issue.

/*
    \file   main.c
    \brief  Main file of the project.
    (c) 2018 Microchip Technology Inc. and its subsidiaries.
    Subject to your compliance with these terms, you may use Microchip software and any
    derivatives exclusively with Microchip products. It is your responsibility to comply with third party
    license terms applicable to your use of third party software (including open source software) that
    may accompany Microchip software.
    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY
    IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS
    FOR A PARTICULAR PURPOSE.
    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP
    HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO
    THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL
    CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT
    OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS
    SOFTWARE.
*/

#define F_CPU 3333333
#define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

static void USART0_sendChar(char c)
{
    while (!(USART0.STATUS & USART_DREIF_bm))
    {
        ;
    }
    USART0.TXDATAL = c;
}

static int USART0_printChar(char c, FILE *stream)
{ 
    USART0_sendChar(c);
    return 0; 
}

static FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);

static void USART0_init(void)
{
    PORTA.DIR |= PIN0_bm;
    
    USART0.BAUD = (uint16_t)USART0_BAUD_RATE(9600); 
    
    USART0.CTRLB |= USART_TXEN_bm;  
    
    stdout = &USART_stream;
}

int main(void)
{
    uint8_t count = 0;
    
    USART0_init();
    
    while (1) 
    {
        printf("Counter value is: %d\n\r", count++);
        _delay_ms(500);
    }
}
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:ATtiny402]
platform = atmelmegaavr
board = ATtiny402
;framework = arduino
upload_speed = 115200
upload_port = COM7
upload_flags =
    --tool
    uart
    --device
    attiny402
    --uart
    $UPLOAD_PORT
    --clk
    $UPLOAD_SPEED
upload_command = pymcuprog write --erase $UPLOAD_FLAGS --filename $SOURCE

Processing ATtiny402 (platform: atmelmegaavr; board: ATtiny402)
---------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelmegaavr/ATtiny402.html
PLATFORM: Atmel megaAVR (1.6.0) > ATtiny402
HARDWARE: ATTINY402 16MHz, 256B RAM, 4KB Flash
PACKAGES:
 - toolchain-atmelavr @ 1.70300.191015 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Compiling .pio\build\ATtiny402\src\main.o
In file included from src\main.cpp:28:0:
src\main.cpp:45:28: sorry, unimplemented: non-trivial designated initializers not supported
 static FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);    
                            ^
src\main.cpp:45:28: sorry, unimplemented: non-trivial designated initializers not supported  
src\main.cpp:45:28: sorry, unimplemented: non-trivial designated initializers not supported  
*** [.pio\build\ATtiny402\src\main.o] Error 1
================================ [FAILED] Took 2.84 seconds ================================

Rename this to src/main.c.

Hi Max
Thank you for responding.
I don’t understand your directive - rename exactly what? - and why for that matter?

This only appears in .cpp files. Not with .c files. Rename your src/main.cpp to src/main.cpp for the baremetal example.

Also, in your original .cpp example, this can impossibly work. You’re remapping the write function to USART0_printChar() but it never even loads the character into the USART0 data register / FIFO. It should be like

Hi Max
I renamed the main.cpp to main.c (please see image)


and the red squiggle disappeared from

FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL,_FDEV_SETUP_WRITE); 

However when I tried to compile, I still get the reference to main.cpp and the error.

Compiling .pio\build\ATtiny402\src\main.o
In file included from src\main.cpp:28:0:
src\main.cpp:45:28: sorry, unimplemented: non-trivial designated initializers not supported
 static FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);
                            ^
src\main.cpp:45:28: sorry, unimplemented: non-trivial designated initializers not supported
src\main.cpp:45:28: sorry, unimplemented: non-trivial designated initializers not supported
*** [.pio\build\ATtiny402\src\main.o] Error 1

How do I change the reference from src\main.cpp:28:0: to src\main.c ??

Best regards

No now that isn’t right at all, you can only use .c in the baremetal project, not in the one using Arduino.

^ – that one.

Thanks - the baremetal project compiled but there was no serial output.
Here’s where I’m at
Source

/*
    \file   main.c
    \brief  Main file of the project.
    (c) 2018 Microchip Technology Inc. and its subsidiaries.
    Subject to your compliance with these terms, you may use Microchip software and any
    derivatives exclusively with Microchip products. It is your responsibility to comply with third party
    license terms applicable to your use of third party software (including open source software) that
    may accompany Microchip software.
    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY
    IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS
    FOR A PARTICULAR PURPOSE.
    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP
    HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO
    THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL
    CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT
    OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS
    SOFTWARE.
*/

#define F_CPU 20000000
#define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

static void USART0_sendChar(char c)
{
    while (!(USART0.STATUS & USART_DREIF_bm))
    {
        ;
    }
    USART0.TXDATAL = c;
}

static int USART0_printChar(char c, FILE *stream)
{ 
    USART0_sendChar(c);
    return 0; 
}

static FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);

static void USART0_init(void)
{
    PORTA.DIR |= PIN0_bm;
    
    USART0.BAUD = (uint16_t)USART0_BAUD_RATE(9600); 
    
    USART0.CTRLB |= USART_TXEN_bm;  
    
    stdout = &USART_stream;
}

int main(void)
{
    uint8_t count = 0;
    
    USART0_init();
    
    while (1) 
    {
        printf("Counter value is: %d\n\r", count++);
        _delay_ms(500);
    }
}

Image from PlatformIO showing src file named main.c

Output from compile

Processing ATtiny402 (platform: atmelmegaavr; board: ATtiny402)
-------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelmegaavr/ATtiny402.html
PLATFORM: Atmel megaAVR (1.6.0) > ATtiny402
HARDWARE: ATTINY402 20MHz, 256B RAM, 4KB Flash
PACKAGES:
 - tool-avrdude-megaavr @ 1.60300.191015 (6.3.0)
 - toolchain-atmelavr @ 1.70300.191015 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Checking size .pio\build\ATtiny402\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]   7.8% (used 20 bytes from 256 bytes)
Flash: [====      ]  40.1% (used 1643 bytes from 4096 bytes)
Configuring upload protocol...
AVAILABLE: jtag2updi
CURRENT: upload_protocol = jtag2updi
Looking for upload port...
Using manually specified: COM7
Uploading .pio\build\ATtiny402\firmware.hex
Connecting to SerialUPDI
Pinging device...
Ping response: 1E9227
Erasing device before writing from hex file...
Writing from hex file...
Writing flash...
Done.
======================================== [SUCCESS] Took 9.44 seconds =======================================

I’m done for today - I’ll pick this up tomorrow - thanks for your responses.

In the longer term, I’m realistically going to need to use Arduino framework for libraries - Am I chasing a dead end with the baremetal approach?

Hi Max
Thanks again for your help. I managed to get printf working within Arduino framework by renaming the src\main.cpp to src\main.c but I am sure there will be Arduino libraries that will insist on C++ so I will tread cautiously.
I was disappointed to learn that using printf chewed up so much flash memory 54.4% - when I commented out the printf line in the main loop(), line 31, the code used up just 18.9%.
Thanks again for helping me get a handle on changing the output stream of the printf but as I only wanted some trivial debug output, I think I’ll stick with my very simplistic sendChar routines.
I am going to keep the working prinf code example should I wish to include it on a project with more flash memory available.
For anyone interested in the working example, please see below:

// uses demo code from Microchip TB3216 <https://ww1.microchip.com/downloads/en/Appnotes/TB3216-Getting-Started-with-USART-DS90003216.pdf>

#include <Arduino.h>
#define F_CPU 20000000
#define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)
#include <util/delay.h>
#include <avr/io.h>
#include <stdio.h>

void USART0_init(void);
void USART0_sendChar(char c);
void USART0_sendString(char *str);
void USART0_sendInteger(int n);
int USART0_printChar(char c, FILE *stream);

static FILE USART_stream = FDEV_SETUP_STREAM(USART0_printChar, NULL, _FDEV_SETUP_WRITE);

void setup() {
  // put your setup code here, to run once://
  USART0_init();
  USART0_sendString("\r\nStarting\r\n");
}

void loop() {
  // put your main code here, to run repeatedly:
  static int val = 10;
  static int count = 0;
  USART0_sendInteger(val);
  USART0_sendString("\r\n");
  val--;
  printf("Counter value is: %d\n\r", count++);
  _delay_ms(500);
}

void USART0_init(void){
  PORTA.DIR &= ~PIN7_bm;  // input
  PORTA.DIR |= PIN6_bm;   // output
  USART0.BAUD = (uint16_t)USART0_BAUD_RATE(115200);
  USART0.CTRLB |= USART_TXEN_bm;
  stdout = &USART_stream;
}

void USART0_sendChar(char c){
  while (!(USART0.STATUS & USART_DREIF_bm));
  USART0.TXDATAL = c;
}

void USART0_sendString(char *str){
  for(size_t i = 0; i < strlen(str); i++){
  USART0_sendChar(str[i]);
  }
}

int USART0_printChar(char c, FILE *stream)
{ 
    USART0_sendChar(c);
    return 0; 
}

void USART0_sendInteger(int n){
  int remainder = 0;
  int sum = 0;
  int digitCount = 0;
  if (n==0){                    // print a 0
    USART0_sendChar(48);
    return;
  }
  if (n<0){                     // if integer is negative
    n= n * -1;                  // make it positive
    USART0_sendChar(45);        // but print a negative sign
  }
  while (n > 0){                // reverse the digits
    sum = (sum * 10) + n % 10;  
    n /= 10;
    digitCount ++;              // count the digits  - the trailing digit may be 0
  }
  n = sum;
  while (digitCount > 0) {
		digitCount--;
    int digit = n % 10;
		n /= 10;
    USART0_sendChar(digit + 48);  // print put each digit
	}
}

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:ATtiny402]
platform = atmelmegaavr
board = ATtiny402
framework = arduino
board_build.f_cpu = 20000000L
upload_speed = 115200
upload_port = COM7
upload_flags =
    --tool
    uart
    --device
    attiny402
    --uart
    $UPLOAD_PORT
    --clk
    $UPLOAD_SPEED
upload_command = pymcuprog write --erase $UPLOAD_FLAGS --filename $SOURCE

Build output

*  Executing task in folder ATtiny402_USART: C:\Users\Peter\.platformio\penv\Scripts\platformio.exe run --target upload --environment ATtiny402 

Processing ATtiny402 (platform: atmelmegaavr; board: ATtiny402; framework: arduino)
-------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelmegaavr/ATtiny402.html
PLATFORM: Atmel megaAVR (1.6.0) > ATtiny402
HARDWARE: ATTINY402 20MHz, 256B RAM, 4KB Flash
PACKAGES:
 - framework-arduino-megaavr-megatinycore @ 2.5.11
 - tool-avrdude-megaavr @ 3.60300.220118 (6.3.0)
 - toolchain-atmelavr @ 3.70300.220127 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 15 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Compiling .pio\build\ATtiny402\src\main.c.o
Linking .pio\build\ATtiny402\firmware.elf
Checking size .pio\build\ATtiny402\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]  13.3% (used 34 bytes from 256 bytes)
Flash: [=====     ]  54.4% (used 2228 bytes from 4096 bytes)
Building .pio\build\ATtiny402\firmware.hex
Configuring upload protocol...
AVAILABLE: jtag2updi
CURRENT: upload_protocol = jtag2updi
Looking for upload port...
Using manually specified: COM7
Uploading .pio\build\ATtiny402\firmware.hex
Connecting to SerialUPDI
Pinging device...
Ping response: 1E9227
Erasing device before writing from hex file...
Writing from hex file...
Writing flash...
Done.
======================================== [SUCCESS] Took 9.26 seconds ========================================

Picture of PlatformIO showing renamed main.c and no red squiggles under FDEV_SETUP_STREAM

Sample output

Starting
10
Counter value is: 0
9
Counter value is: 1
8
Counter value is: 2
7
Counter value is: 3
6
Counter value is: 4
5
Counter value is: 5
4
Counter value is: 6
3
Counter value is: 7
2
Counter value is: 8
1
Counter value is: 9
0
Counter value is: 10
-1
Counter value is: 11
-2
Counter value is: 12
-3
Counter value is: 13
-4
Counter value is: 14
-5
Counter value is: 15
-6