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

Ссылаются они на строку:
Google приводит нас сюда. Designated Initializers описаны тут.
Макрос FDEV_SETUP_STREAM не компилируется в С++, так как он использует Designated Initializers. Для решения проблемы предлагается использовать полную инициализацию. Вернемся к файлу stdio.h. Чуть ниже обьявления FDEV_SETUP_STREAM находится прототип:
И комментарий к нему:
Попробуем воспользоваться им вместо макроса FDEV_SETUP_STREAM. Код проекта примет следующий вид:
Попробуем собрать проект… Build succeeded. Подключаем микроконтроллер к COM порту. Открываем терминал, смотрим.

Надеюсь, данная статья поможет начинающим в отладке.
Создадим пустой С++ 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 и видим следующие ошибки:

Ссылаются они на строку:
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 порту. Открываем терминал, смотрим.

Надеюсь, данная статья поможет начинающим в отладке.