【方法】STM32F103C8單片機在Keil 5環境下使用C++編寫程序,並將printf和cout重定向到串口

程序下載地址:https://pan.baidu.com/s/1SWQRwVQ53bvI50Iqkgpquw(提取碼:s7d6)

首先,用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 <stdio.h>
#include <stm32f1xx.h>
#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=%d\n", returncode);
    while (1);
  }
  
  void _ttywrch(int ch)
  {
    if (ch == '\n')
      HAL_UART_Transmit(&huart1, (uint8_t *)"\r\n", 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 <iostream>
#include <typeinfo>
#include <vector>
#include <stdio.h>
#include <stm32f1xx.h>
#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 <typename T>
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<string> items;
  vector<string>::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[i] = items.at(i).length();
  for (i = 0; i < n; i++)
    cout << arr[i] << " ";
  cout << endl;
  delete[] arr;
  
  // 類
  Test t = 50;
  t.display();
  
  // 引用
  Test &u = t;
  u.display();
  
  // scanf輸入
  printf("Please input: ");
  scanf("%d", &n);
  printf("2n=%d\n", 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 printf\n");
  printf("SystemCoreClock=%u\n", SystemCoreClock);
  
  cout << "STM32F103C8 cout" << endl;
  cout << "SystemCoreClock=" << SystemCoreClock << endl;
  
  // 函數重載
  further_test(15);
  further_test("this");
  further_test();
  
  return 0;
}

【common.cpp】

#include <stdio.h>
#include <stm32f1xx.h>
#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 %d\n", __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
[i] 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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章