Search code examples
cembeddedavr

AVR mega2560 printf() and fgetc() don't work


I'm trying to learn about embedded programming, so I bought an arduino mega2560. I didn't really like the arduino IDE (Because it feels a little too basic and abstracted, at least for me :)) so I started programming it using pure C and the avr toolkit (avrdude and such). I'm trying to write a program that reads your name, and then blinks the LED for each character in your name.

The program's basic outline is:

  1. Turn the LED pin to output mode
  2. **printf() "Enter your name"
  3. **Read the user's name using fgetc, realloc and such (I'm not using scanf() because scanf() with a buffer can cause a buffer overflow)
  4. Blink the LED for each character in the user's name

The problems (Indicated by **) are that I can't see printf's output and fgetc asking for input. How to fix this?

Thanks!

Edit: Here's the code:

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

#ifndef STDIN
#define STDIN 0
#endif

int main(void) {
        char *name = (char *)malloc((int)NULL);
        char c;
        int cnt;
        int i;

        cnt = 1;

        DDRB |= (1 << DDB7);

        printf("Enter your name: ");

        while ((c = fgetc(STDIN)) != '\n') {
                name = (char *)realloc(name, cnt);
                strncat(name, &c, 1);

                cnt++;
        }

        for (i = 0; i < strlen(name); ++i) {
                PORTB |= (1 << PB7);
                _delay_ms(1000);
                PORTB &= ~(1 << PB7);
        }

        return 0;
}

Solution

  • The stdio library for MCUs where the stdout, stdin need not exist at all and where they do they are necessarily dependent on the hardware and available I/O and are design decisions of the project developer not the responsibility of the library designer.

    Typically for an embedded library there will be a porting or "glue" layer to map the library to the hardware dependencies, and if you do not supply the necessary functions they will be linked to generic "do nothing" stubs.

    The specific stub-function overrides you need to provide will depend of the toolchain and specifically the C library you are using. And the means by which you "install" them differs depending on the toolchain. Some use "weak-links" that you simply provide overrides with the same signature, others use structures with function pointers to the low-level I/O functions. For example if you are using AVR Libc, you simply need to provide a get-char / put-char functions use them in a FILE structure assigned ot the stdin, stdout streams as described in the documentation.

    For example:

    #include <stdio.h>
    
    // Output character to stdout device
    static int std_putchar(char c, FILE *stream)
    {
        ...
        return 0;
    }
    
    // Input character from stdin device
    static int std_getchar(char c, FILE *stream)
    {
        ...
        return rxchar ;
    }
    
    // Create stdio stream
    static FILE stdio_stream = FDEV_SETUP_STREAM( std_putchar, std_getchar,
                                                  _FDEV_SETUP_RW ) ;
    
    int main(void)
    {
        stdout = &stdio_stream ;
        stdin = &stdio_stream ;
        stderr = &stdio_stream ;
    
        printf( "System Start - hello\n") ;
    
        // Echo input
        for(;;)
        {
            putchar( getchar() ) ;
        }
    } 
    

    You can implement the low level character I/O functions in any way you wish to suit your system, to use a UART, keypad, LCD display, debugger interface - whatever.