printf в микроконтроллере STM32 и других.

Эта короткая заметка будет небольшим мануалом по налаживанию работы таких привычных си программистам функций, как printf, scanf и их модификаций.

Не думаю, сто стоит рассказывать о функциях printf и scanf. Раз читаете эту заметку, то в курсе зачем они, знаете плюсы, и возможно минусы. Обсуждение и того и другого можно расписать на целую статью, я лишь хочу показать как их завести в прошивке для микроконтроллера.

Объектом испытания будет выступать микроконтроллер фирмы STMicroelectronics STM32F205RGT6. Почему не ванильный f103? Всё просто, отладку с ним переделал под 205 чип, да и разрабатываемое устройство базируется на 205 чипе. Хотя это на самом деле и не важно, реализация полностью кросплатформенная.

Ну что поехали.

Первое что нужно для использования функций это подключить заголовочный файл директивой 

#include <stdio.h>

Теперь необходимо реализовать две функции:

/**
  * @brief  Retargets the C library printf function to the USART or USBD.
  */
int fputc(int ch, FILE *f) //-V2537
{
    char item = (char)ch;
    #ifdef USE_FREERTOS
        xQueueSendToBack(stdout_queue, &item, 0);
    #else
        (void)HAL_UART_Transmit(&huart1, (uint8_t *)&item, 1, 1000);
    #endif
    return ch;
}
/**
  * @brief  Retargets the C library scanf function to the USART or USBD.
  */
int fgetc(FILE *f)
{
    uint8_t ch;
    #ifdef USE_FREERTOS
        return (xQueueReceive(stdin_queue, &ch, portMAX_DELAY) == pdPASS) ? (int)ch : (int)'\0';
    #else
        (void)HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);
        return((int)ch);
    #endif
}

Готово.

Поясню код. В проекте использую FreeRTOS, и работы с принимаемыми и передаваемыми данными веду через очереди, которые обрабатываю в соответствующих задачах.

Задача передачи выглядит следующим образом:

void StdoutTask(void *pvParameters)
{
    for(;;)
    {
        STDOUT_Flush();
    }
    __ASM volatile("BKPT #01");
    vTaskDelete( NULL );
}

void STDOUT_Flush(void)
{
    static uint8_t buf[ STDOUT_QUEUE_SIZE ];
    uint16_t length = 0;

    if (xQueueReceive(stdout_queue, &buf[length], portMAX_DELAY) == pdPASS)
    {
        while((++length < STDOUT_QUEUE_SIZE) && (xQueueReceive(stdout_queue, &buf[length], 0) == pdPASS)) {};
        (void)HAL_UART_Transmit_IT(&huart1, buf, length);
        while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY && HAL_UART_GetState(&huart1) != HAL_UART_STATE_BUSY_RX)
        {
            vTaskDelay( TIME_MSEC );
        }
    }
}

Подробно объяснять думаю нет необходимости, в цикле задачи выполняется функция, которая блокирует задачу до момента появления данных в очереди, затем считывает и посылает через USART1.

Задачу для получения приводить не буду, тянет на отдельную статью - реализует CLI (интерфейс командной строки), по сути громадный парсер команд, которые может выполнять железяка.

Кстати у FreeRTOS есть готовая библиотека для реализации командной строки. Возможно скоро о ней напишу Вот заметка о ней.

Надеюсь пригодится данная заметка. Хорошего кодинга 😉