Pull to refresh

Прикручиваем функции stdio.h к С++ проекту

Я бы хотел поделиться тем, как «научить» стандартные функции, такие как printf, работать в вашем C++ проекте, на примере проекта в Atmel Studio. Так как JTAG отладчика у меня нет, а отлаживать ПО как-то нужно, решил выводить информацию по UART. Сам по себе передатчик может вывести лишь байт, дебажная инфомация же представлена в виде строк. Существует несколько библиотек для буферизированной работы с UART, но я хочу воcпользоваться именно printf из stdio.h.

Создадим пустой С++ executable проект в Atmel Studio. Пропишем:

#include <stdio.h>

Перейдём к implementation и обратим внимание на пример:

<h4>Example</h4>

    \code
    #include <stdio.h>

    static int uart_putchar(char c, FILE *stream);

    static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
                                             _FDEV_SETUP_WRITE);

    static int
    uart_putchar(char c, FILE *stream)
    {

      if (c == '\n')
        uart_putchar('\r', stream);
      loop_until_bit_is_set(UCSRA, UDRE);
      UDR = c;
      return 0;
    }

    int
    main(void)
    {
      init_uart();
      stdout = &mystdout;
      printf("Hello, world!\n");

      return 0;
    }
    \endcode

Попробуем вставить этот код в наш проект:

#define FOSC 7372800UL // Clock Speed
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1

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

static int uart_putchar(char c, FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

void init_uart(unsigned int ubrr)
{
	/*Set baud rate */
	UBRR0H = (unsigned char)(ubrr>>8);
	UBRR0L = (unsigned char)ubrr;
	/*Enable receiver and transmitter */
	UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
	/* Set frame format: 8data, 2stop bit */
	UCSR0C = (1<<USBS0)|(1<<UCSZ00)|(1<<UCSZ01);
}

static int
uart_putchar(char c, FILE *stream)
{

	if (c == '\n')
	uart_putchar('\r', stream);
	while ( !( UCSR0A & (1<<UDRE0)) )
	;
	/* Put data into buffer, sends the data */
	UDR0 = c;
	return 0;
}

int main(void)
{
    init_uart(MYUBRR);
    stdout = &mystdout;
    printf("Hello, world!\n");

    return 0;
}

Жмем build solution и видим следующие ошибки:

image

Ссылаются они на строку:

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

Google приводит нас сюда. Designated Initializers описаны тут.

Макрос FDEV_SETUP_STREAM не компилируется в С++, так как он использует Designated Initializers. Для решения проблемы предлагается использовать полную инициализацию. Вернемся к файлу stdio.h. Чуть ниже обьявления FDEV_SETUP_STREAM находится прототип:

extern FILE *fdevopen(int (*__put)(char), int (*__get)(void),
                      int __opts __attribute__((unused)));

И комментарий к нему:

/*
 * Declare prototype for the discontinued version of fdevopen() that
 * has been in use up to avr-libc 1.2.x.  The new implementation has
 * some backwards compatibility with the old version.
 */

Попробуем воспользоваться им вместо макроса FDEV_SETUP_STREAM. Код проекта примет следующий вид:

#define FOSC 7372800UL // Clock Speed
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1

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

FILE * uart_str;

static int uart_putchar(char c, FILE *stream);
static int uart_getchar(FILE *stream);

void init_uart(unsigned int ubrr)
{
	/*Set baud rate */
	UBRR0H = (unsigned char)(ubrr>>8);
	UBRR0L = (unsigned char)ubrr;
	/*Enable receiver and transmitter */
	UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
	/* Set frame format: 8data, 2stop bit */
	UCSR0C = (1<<USBS0)|(1<<UCSZ00)|(1<<UCSZ01);
}

static int
uart_putchar(char c, FILE *stream)
{

	if (c == '\n')
	uart_putchar('\r', stream);
	while ( !( UCSR0A & (1<<UDRE0)) )
	;
	/* Put data into buffer, sends the data */
	UDR0 = c;
	return 0;
}

static int
uart_getchar(FILE *stream)
{
	/* Wait for data to be received */
	while ( !(UCSR0A & (1<<RXC0)) )
	;
	/* Get and return received data from buffer */
	char data = UDR0; //Temporarly store received data
	if(data == '\r')
	data = '\n';
	uart_putchar(data, stream); //Send to console what has been received, so we can see when typing
	return data;
}

int main(void)
{
    init_uart(MYUBRR);
	uart_str = fdevopen(uart_putchar, uart_getchar); //send , receive functions
    stdout = uart_str;
    printf("Hello, world!\n");

    return 0;
}

Попробуем собрать проект… Build succeeded. Подключаем микроконтроллер к COM порту. Открываем терминал, смотрим.

image

Надеюсь, данная статья поможет начинающим в отладке.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.