Вопрос или проблема
Я работал с Nucleo STM32l433RCP и у меня была отличная настройка, где я использовал функцию _write, которая принимала символы, буферизированные printf, и передавала их по UART. Все это было написано на C и работало замечательно. С тех пор я перешел на C++ для совместимости с группой, с которой я работаю над проектом, и это больше не работает. Printf все еще вызывает функцию записи при сбросе, но буфер, похоже, заполнен мусором. Я работаю без операционной системы и не использую stm32IDE или инструменты HAL, которые с ним поставляются.
Единственные изменения, которые я сделал при переходе с C на C++, это запуск _write и моего обработчика сброса как кода на C с использованием extern “C” (без чего ничего не происходит).
Ниже представлен мой main.cpp, пожалуйста, не обращайте внимания на некоторую неприглядную форматировку
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define GPIOBS 0x48000400
#define RCC_APB2ENR 0x4002104C
// #define ODR 0x14
// #define OTR 0x04
#define SERIAL_PORT "/dev/ttyACM0"
#include "../bsp/stm32l433xx.h"
uint32_t SystemCoreClock = 0;
extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss;
uint32_t arr[2000];
extern "C" {
// Обработчик сброса: установить указатель стека и перейти к main().
__attribute__((naked)) void reset_handler(void) {
// Установить указатель стека в значение 'конец стека'.
__asm__("LDR r0, =_estack\n\t"
"MOV sp, r0");
// Перейти к main().
__asm__("B main");
}
}
extern "C" {
int __io_putchar(int ch){
while (!(USART2->ISR & USART_ISR_TXE)) {};
USART2->TDR = ch;
return ch;
}
int _write(int handle, const char* data, int size) {
int count = size;
while (count--) {
__io_putchar(*data++);
}
while (!(USART2->ISR & USART_ISR_TXE)) {};
USART2->TDR = 74;
while (!(USART2->ISR & USART_ISR_TXE)) {};
USART2->TDR = 10;
while (!(USART2->ISR & USART_ISR_TXE)) {};
USART2->TDR = 13;
return size;
}
void init(){
// копируем данные из флеш-памяти в RAM
memcpy(&_sdata, &_sidata, 4*(&_edata - &_sdata));
// Очищаем секцию .bss в RAM.
memset(&_sbss, 0x00, 4* (&_ebss -&_sbss));
for (int i = 0; i < 10000; ++i){
arr[i] = i;
}
// Включаем модуль плавающей запятой.
SCB->CPACR |= (0xF << 20);
RCC->AHB2ENR |= 7;
// Включаем модуль плавающей запятой.
SCB->CPACR |= (0xF << 20);
// Источник тактового сигнала по умолчанию - "мультискоростной" внутренний генератор.
// Переключаемся на 16 МГц HSI генератор.
RCC->CR |= (RCC_CR_HSION);
while (!(RCC->CR & RCC_CR_HSIRDY)) {
};
RCC->CFGR &= ~(RCC_CFGR_SW);
RCC->CFGR |= (RCC_CFGR_SW_HSI);
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) {
};
SystemCoreClock = 16000000;
RCC->APB1ENR1 |= (RCC_APB1ENR1_USART2EN);
RCC->AHB2ENR |= (RCC_AHB2ENR_GPIOAEN);
// Конфигурируем пины A2, A15 для USART2 (AF7, AF3).
GPIOA->MODER &= ~((0x3 << (2 * 2)) | (0x3 << (15 * 2)));
GPIOA->MODER |= ((0x2 << (2 * 2)) | (0x2 << (15 * 2)));
GPIOA->OTYPER &= ~((0x1 << 2) | (0x1 << 15));
GPIOA->OSPEEDR &= ~((0x3 << (2 * 2)) | (0x3 << (15 * 2)));
GPIOA->OSPEEDR |= ((0x2 << (2 * 2)) | (0x2 << (15 * 2)));
GPIOA->AFR[0] &= ~((0xF << (2 * 4)));
GPIOA->AFR[0] |= ((0x7 << (2 * 4)));
GPIOA->AFR[1] &= ~((0xF << ((15 - 8) * 4)));
GPIOA->AFR[1] |= ((0x3 << ((15 - 8) * 4)));
uint16_t uartdiv = SystemCoreClock / 9600;
USART2->BRR = uartdiv;
USART2->CR1 |= (USART_CR1_RE | USART_CR1_TE | USART_CR1_UE);
//printf("starting, Clock speed is: %lu \r\n", SystemCoreClock);
// Тест LED
GPIOB->MODER &= (~(1 << 27));
GPIOB->MODER |= (1 << 26);
GPIOB->ODR |= (1 << 13);
printf("Test\n");
}
}
int main() {
init();
int x = 0;
;
while (1) {
GPIOB->ODR |= (1 << 13);
for (int i = 0; i < 500000; i++) {
};
GPIOB->ODR &= (~(1 << 13));
for (int i = 0; i << 500000; i++) {
};
x++;
printf("APPLE\n");// %d\r\n", x);
_write(0, "APE TOGETHER",12);
fflush(stdout);
}
}
В настоящее время моя функция _write также выводит символ новой строки и возврат каретки, чтобы результаты были менее ужасными при просмотре вывода по uart. Я протестировал все на C, и это работает.
Я компилирую с использованием arm-none-eabi-g++/gcc в зависимости от того, является ли файл C или нет.
Спасибо
Ответ или решение
Когда вы переходите с C на C++ в проекте на основе STM32L433RCP, и замечаете, что функция printf
начинает выводить ненормативный (мусорный) контент, это обычно связано с различиями в обработке буферизации и вызовах функций ввода-вывода в этих двух языках. Давайте подробнее разберем возможные причины и пути решения этой проблемы.
1. Различия в компиляторах и настройках
Сначала убедитесь, что при компиляции проекта вы используете правильные настройки для C++. Компилятор C++ может по умолчанию применять другие стандартные библиотеки, если это не указано явно. Например, проверьте, что вы используете те же опции компилятора для C и C++.
2. Буферизация
В C стандартная библиотека работает с буферизацией вывода. Когда вы вызываете printf
, вывод может не быть немедленно отправлен на UART, пока не произойдет ‘flush’ (очистка буфера). В вашем коде это происходит, когда вы вызываете fflush(stdout)
.
В C++ необходимо убедиться, что вы также корректно обрабатываете выходные данные, что может быть не так очевидно, как в C. Например, в C++ можно использовать std::cout
и другие механизмы вывода, которые имеют свои правила буферизации.
3. Аргументы функции _write
Функция _write
принимает const char* data
, но передается ли ей корректный указатель на данные? В C++ убедитесь, что строка, которую вы передаете, корректно завершается нулевым символом ('\0'
). Это особенно важно, если вы передаете строки, которые могут быть составлены динамически или изменяться.
4. Обработка переменных
Вы можете увидеть "мусорные" данные из-за неправильного обращения с переменными. Убедитесь, что в вашем коде вы не переписываете или не используете переменные, которые могут быть неправильно инициализированы. Например, массив arr[2000]
должен быть правильно инициализирован перед использованием.
5. Использование extern "C"
Вы правильно используете extern "C"
для функций, чтобы предотвратить "обфускацию" имён при компиляции C++. Убедитесь, что все функции, которые взаимодействуют с традиционными C (например, _write
, __io_putchar
), обернуты в extern "C"
, и что они корректно вызываются.
6. Проверка входящих данных
Наконец, добавьте отладочные сообщения и проверьте содержимое буфера перед его передачей в UART. Используйте временные переменные для хранения текста перед его передачей:
char buffer[256];
snprintf(buffer, sizeof(buffer), "APPLE %d\n", x);
_write(0, buffer, strlen(buffer));
Итог
Основная проблема, с которой вы столкнулись, вероятно, связана с различиями в буферизации и обработке строк в C и C++. Проверьте корректность передачи данных, инициализацию переменных и вызовы к функциям вывода. Также убедитесь в исправности конфигурации компилятора. Применив эти рекомендации, вы сможете успешно отладить свою систему вывода для работы с printf
в C++.