程序下載地址: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