使用CubeMX在生成代码时,会生成main.c文件。如果使用c++语言编程,将main.c改为main.cpp。CubeMX再次生成代码,会新建一个main.c文件。所以在重新生成代码前,需要将main.cpp改为main.c,代码更新后再将main.c改为main.cpp。 这样频繁的修改文件名,让人厌烦。
这里将实现c调用c++方法,避免修改文件名,实现CubeIDE1.8 c/c++混合编程。
使用CubeIDE生成项目时,选择c++语言,就可以编译c++代码。
使用串口DMA方式收发数据为例,说明如何使用c/c++混合编程。
新建项目,并且配置好DMA和UART1。
新增3个类:
RunCPP:c代码使用这个文件提供的方法调用c++的类方法。严格来说RunCPP不是类。
McuCPP:全局类,单例模式。
SerialPort:串口类,管理接收缓冲与发送缓冲。
类文件保存目录如下:
RunCPP源码
RunCPP.h
#ifndef RUNCPP_H_
#define RUNCPP_H_
#include "stm32f1xx_hal.h"
#ifdef __cplusplus //使用C语言的方式编译方法名。
extern "C" {
#endif
void CPP_UART1_Init(UART_HandleTypeDef* huart,DMA_HandleTypeDef* hdma_rx,DMA_HandleTypeDef* hdma_tx); //建立SerialPort、缓冲区。
void ResponseUart1Data(); //接收到数据后中断处理函数调用的方法。
#ifdef __cplusplus
}
#endif
#endif /* RUNCPP_H_ */
RunCPP.cpp
#include "RunCPP.h"
#include "McuCPP.h"
void CPP_UART1_Init(UART_HandleTypeDef* huart,DMA_HandleTypeDef* hdma_rx,DMA_HandleTypeDef* hdma_tx)
{
McuCPP::GetInstance().CPP_UART1_Init(huart,hdma_rx,hdma_tx);
}
void ResponseUart1Data()
{
McuCPP::GetInstance().GetUart1()->ResponseUartData();
}
McuCPP类源码
McuCPP.h
#ifndef MCUCPP_H_
#define MCUCPP_H_
#include <stdio.h>
#include "main.h"
#include "stm32f1xx_it.h"
#include "SerialPort.h"
class McuCPP {
public:
//单例模式
static McuCPP& GetInstance();
virtual ~McuCPP();
protected:
McuCPP();
//禁止拷贝构造
McuCPP(const McuCPP& mcu);
//禁止赋值
McuCPP& operator = (const McuCPP& mcu);
private:
SerialPort* huart1;
public:
SerialPort* GetUart1() {return huart1;}
void CPP_UART1_Init(UART_HandleTypeDef* huart,DMA_HandleTypeDef* hdma_rx,DMA_HandleTypeDef* hdma_tx)
{
huart1 = new SerialPort(100);
huart1->SetUart(huart,hdma_rx,hdma_tx);
}
void CPP_UART1_Dispose()
{
if(huart1 != nullptr) { delete huart1; huart1 = nullptr;}
}
};
#endif /* MCUCPP_H_ */
McuCPP.cpp
#include "McuCPP.h"
// Return the instance pointer
McuCPP& McuCPP::GetInstance()
{
static McuCPP instance;
return instance;
}
McuCPP::McuCPP():
huart1(nullptr)
{
}
McuCPP::~McuCPP() {
}
SerialPort类源码
SerialPort.h
#ifndef SERIALPORT_H_
#define SERIALPORT_H_
#include <stdio.h>
#include "main.h"
#include "stm32f1xx_it.h"
#include <cstring>
class SerialPort {
public:
SerialPort(int bufferlength);
virtual ~SerialPort();
protected:
//禁止拷贝构造
SerialPort(const SerialPort& serialport);
//禁止赋值
SerialPort& operator = (const SerialPort& serialport);
private:
UART_HandleTypeDef* huart; //串口句柄
DMA_HandleTypeDef* dma_rx;
DMA_HandleTypeDef* dma_tx;
uint8_t* rx_buffer; //接收数据缓冲区地址
uint8_t* tx_buffer; //发送数据缓冲区地址
int bufferlength; //接收数据缓冲区长度
public:
void SetUart(UART_HandleTypeDef* huart,DMA_HandleTypeDef* dma_rx,DMA_HandleTypeDef* dma_tx)
{
this->huart = huart;
this->dma_rx = dma_rx;
this->dma_tx = dma_tx;
__HAL_UART_ENABLE_IT(this->huart, UART_IT_IDLE); //使能IDLE中断
HAL_UART_Receive_DMA(this->huart,rx_buffer,bufferlength); //设置串口接收缓冲,开始接收数据
}
void ResponseUartData()
{
if(huart != nullptr)
{
uint32_t tmp_flag = __HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE); //获取IDLE标志
if((tmp_flag != RESET))//idle标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(huart);//清除标志位
HAL_UART_DMAStop(huart); //
uint32_t temp = __HAL_DMA_GET_COUNTER(dma_rx);// 获取DMA中未传输的数据个数
int rx_len = bufferlength - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
if(rx_len>0)
{
memcpy(tx_buffer,rx_buffer,rx_len);
HAL_UART_Transmit_DMA(huart, tx_buffer, rx_len);
memset(rx_buffer,0,rx_len);
}
HAL_UART_Receive_DMA(huart,rx_buffer,bufferlength);//重新打开DMA接收
}
}
}
};
#endif /* SERIALPORT_H_ */
SerialPort.cpp
#include <SerialPort.h>
#include <stdlib.h>
SerialPort::SerialPort(int bufferlength):
huart(nullptr),
rx_buffer(nullptr),
bufferlength(bufferlength)
{
rx_buffer = (uint8_t*)malloc(bufferlength);
tx_buffer = (uint8_t*)malloc(bufferlength);
}
SerialPort::~SerialPort()
{
if(rx_buffer != nullptr) { delete rx_buffer; rx_buffer = nullptr; }
if(tx_buffer != nullptr) { delete tx_buffer; tx_buffer = nullptr; }
if(huart!= nullptr) {huart = nullptr;}
bufferlength = 0;
}
增加include目录:
在CubeIDE生成的代码中使用c++类:
main.h
/* USER CODE BEGIN Includes */
#include "RunCPP.h"
/* USER CODE END Includes */
main.c
int main(void)
{
......
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init(); //注意初始化顺序
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//初始化串口类SerialPort、缓冲
CPP_UART1_Init(&huart1,&hdma_usart1_rx,&hdma_usart1_tx);
/* USER CODE END 2 */
......
}
stm32f1xx_it.c
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
if(USART1 == huart1.Instance)
{
ResponseUart1Data(); //调用c++的处理方法。
}
/* USER CODE END USART1_IRQn 1 */
}
通过以上的实现方式,串口中断处理程序放到了SerialPort类的ResponseUartData方法里,将接口的设置与数据处理解耦,CubeIDE生成的代码用于接口设置,SerialPort类代码用于数据的业务处理。
SerialPort是C++类,可以使用STL库快速的实现复杂的数据缓冲,例如多个串口使用同一个数据缓冲,或者多个不同的接口使用同一个数据缓冲,也可以使用C++设计模式,实现业务逻辑。