STM32
直播中

王玉兰

7年用户 1323经验值
私信 关注
[问答]

STM32F103C8单片机如何使用C++编写程序并将printf和cout重定向到串口?

STM32F103C8单片机如何使用C++编写程序并将printf和cout重定向到串口?

回帖(1)

温洁

2021-12-2 11:34:05
首先,用pragma指令禁用半主机模式,防止调用printf就出现HardFault。然后在std命名空间中重写fgetc、fputc、fclose、fseek和fflush函数,还要重写__stdin、__stdout和__stderr这三个全局变量。
_sys_exit和_ttywrch也要重写,前者是main函数返回后调用的函数,_ttywrch是出现错误时往串口打印字符的函数。
_sys_exit函数不允许返回,必须以无限循环结尾。
另外,工程属性里面不要勾选Use MicroLIB,这个选项和C++是不兼容的!!即使使用C语言,也能在不勾选Use MicroLIB的情况下printf输出到串口

【cout.cpp】


#include
#include
#include "common.hpp"

#pragma import(__use_no_semihosting) // 禁用半主机模式

// 请不要勾选Use MicroLib
#ifdef __MICROLIB
#error "Please do not use MicroLib!"
#endif

extern "C"
{
  void _sys_exit(int returncode)
  {
    printf("Exited! returncode=%dn", returncode);
    while (1);
  }
  
  void _ttywrch(int ch)
  {
    if (ch == 'n')
      HAL_UART_Transmit(&huart1, (uint8_t *)"rn", 2, HAL_MAX_DELAY);
    else
      HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  }
}

namespace std
{
  struct __FILE
  {
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
  };
  
  FILE __stdin = {0};
  FILE __stdout = {1};
  FILE __stderr = {2};
  
  int fgetc(FILE *stream)
  {
    int c = 0;
   
    if (stream->handle == 0)
    {
      while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == RESET);
      HAL_UART_Receive(&huart1, (uint8_t *)&c, 1, HAL_MAX_DELAY);
      
      _ttywrch((c == 'r') ? 'n' : c); // 回显
      return c;
    }
    return EOF;
  }
  
  int fputc(int c, FILE *stream)
  {
    if (stream->handle == 1 || stream->handle == 2)
    {
      _ttywrch(c);
      return c;
    }
    return EOF;
  }
  
  int fclose(FILE * stream)
  {
    return 0;
  }
  
  int fseek(FILE *stream, long int offset, int whence)
  {
    return -1;
  }
  
  int fflush(FILE *stream)
  {
    if (stream->handle == 1 || stream->handle == 2)
      while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) == RESET);
    return 0;
  }
}
另外,启动文件startup_stm32f103xb.s中默认分配的堆空间太小,必须要改大,否则将无法进入main函数。如果程序运行过程中出现HardFault,说明堆空间仍然不够,还需要继续改大。


Heap_Size       EQU     0x0001000



以下是测试代码:


【main.cpp】


#include
#include
#include
#include
#include
#include "common.hpp"

using namespace std;

// 类
class Test
{
  private:
    int num;
  public:
    Test(int num) : num(num) {}
   
    void display(void)
    {
      cout << "num=" << num << endl;
    }
};

// 函数模板
template
static void further_test(T a)
{
  cout << "[" << typeid(a).name() << "] a=" << a << endl;
}

// 字符串替换
static void str_replace(string search, string replace, string &subject)
{
  string::size_type pos = 0;
  
  for (pos = 0; (pos = subject.find(search, pos)) != string::npos; pos += replace.length())
    subject.replace(pos, search.length(), replace);
}

static void further_test(void)
{
  // string字符串
  string str = "this is";
  str.append(" a string");
  str_replace("is", "[IS]", str);
  cout << str << " len=" << str.length() << endl;
  
  // vector容器
  vector items;
  vector::iterator iter;
  items.push_back("fooo");
  items.push_back("bar");
  items.push_back("welcome");
  cout << "size=" << items.size() << endl;
  for (iter = items.begin(); iter != items.end(); ++iter)
    cout << *iter << " ";
  cout << endl;
  
  // new和delete运算符
  int i;
  int n = items.size();
  int *arr = new int[n];
  for (i = 0; i < n; i++)
    arr = items.at(i).length();
  for (i = 0; i < n; i++)
    cout << arr << " ";
  cout << endl;
  delete[] arr;
  
  // 类
  Test t = 50;
  t.display();
  
  // 引用
  Test &u = t;
  u.display();
  
  // scanf输入
  printf("Please input: ");
  scanf("%d", &n);
  printf("2n=%dn", 2 * n);
  
  // cin输入
  cout << "Please input: ";
  cin >> n;
  cout << "3n=" << 3 * n << endl;
}

// s文件中的Heap_Size必须改大,不能采用默认值,否则进不了main函数
// 如果出现Hard Error, 则说明还需要继续改大
int main(void)
{
  HAL_Init();
  
  clock_init();
  usart_init(115200);
  
  printf("STM32F103C8 printfn");
  printf("SystemCoreClock=%un", SystemCoreClock);
  
  cout << "STM32F103C8 cout" << endl;
  cout << "SystemCoreClock=" << SystemCoreClock << endl;
  
  // 函数重载
  further_test(15);
  further_test("this");
  further_test();
  
  return 0;
}
【common.cpp】


#include
#include
#include "common.hpp"

UART_HandleTypeDef huart1;

extern "C"
{
#ifdef USE_FULL_ASSERT
  // HAL库参数错误警告
  void assert_failed(uint8_t *file, uint32_t line)
  {
    printf("%s: file %s on line %dn", __FUNCTION__, file, line);
    while (1);
  }
#endif
  
  void HardFault_Handler(void)
  {
    printf("Hard Error!n");
    while (1);
  }
  
  void SysTick_Handler(void)
  {
    HAL_IncTick();
  }
}

// 配置系统和总线时钟
void clock_init(void)
{
  HAL_StatusTypeDef status;
  RCC_ClkInitTypeDef clk = {0};
  RCC_OscInitTypeDef osc = {0};

  // 外部晶振为8MHz, 配置PLL倍频器倍频9倍后是72MHz
  osc.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  osc.HSEState = RCC_HSE_ON;
  osc.PLL.PLLMUL = RCC_PLL_MUL9;
  osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  osc.PLL.PLLState = RCC_PLL_ON;
  status = HAL_RCC_OscConfig(&osc);
  
  // 如果外部晶振启动失败, 则改用内部的8MHz晶振, 经过2分频后倍频16倍, 变成64MHz
  if (status != HAL_OK)
  {
    osc.HSEState = RCC_HSE_OFF;
    osc.PLL.PLLMUL = RCC_PLL_MUL16;
    osc.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
    HAL_RCC_OscConfig(&osc);
  }
  
  // ADC时钟不能超过14MHz, 所以需要6分频, 72MHz经过分频后是12MHz
  __HAL_RCC_ADC_CONFIG(RCC_ADCPCLK2_DIV6);
  
  // 配置AHB和APB2总线时钟为72MHz, APB1总线时钟为36MHz
  clk.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  clk.AHBCLKDivider = RCC_SYSCLK_DIV1;
  clk.APB1CLKDivider = RCC_HCLK_DIV2;
  clk.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_2);
}

void usart_init(int baud_rate)
{
  GPIO_InitTypeDef gpio = {0};
  
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_USART1_CLK_ENABLE();
  
  gpio.Mode = GPIO_MODE_AF_PP;
  gpio.Pin = GPIO_PIN_9;
  gpio.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &gpio);
  
  huart1.Instance = USART1;
  huart1.Init.BaudRate = baud_rate;
  huart1.Init.Mode = UART_MODE_TX_RX;
  HAL_UART_Init(&huart1);
}
【common.hpp】


#ifndef __COMMON_H
#define __COMMON_H

#ifdef HAL_UART_MODULE_ENABLED
extern UART_HandleTypeDef huart1;
#endif

void clock_init(void);
void usart_init(int baud_rate);

#endif
程序运行结果:


STM32F103C8 printf
SystemCoreClock=72000000
STM32F103C8 cout
SystemCoreClock=72000000
a=15
[PKc] a=this
th[IS] [IS] a string len=20
size=3
fooo bar welcome
4 3 7
num=50
num=50
Please input: 123
2n=246
Please input: -456
3n=-1368
Exited! returncode=0
举报

更多回帖

×
20
完善资料,
赚取积分