使用C和C++进行混合编程
在做毕设过程中,用到了八个相同的传感器,传感器的通信协议都是一样的,固然直接搞八个差不多的文件来驱动传感器是没问题的,但是有一种更简洁的方式,那就是使用C++,来复用通信程序; 参考:https://zhuanlan.zhihu.com/p/115068898 环境说明 使用的单片机芯片是STM32F103C6T6; Keil编译器版本为AC6; Keil的配置如下: 然后是将C++源文件配对的头文件改为使用C++编译器进行编译; 源码编写 注意把main文件的后缀也改为.cpp,否则会出错; 头文件: /* * @Author : fan-pengfei 2253770787@qq.com * @Date : 2023-03-07 13:43:38 * @LastEditors : fan-pengfei 2253770787@qq.com * @LastEditTime : 2023-03-07 14:41:29 * @FilePath : \Core\Inc\ds18b20.h * @Description : * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. */ #ifndef __DS18B20_H #define __DS18B20_H #include "main.h" #include "tim.h" class DS18B20_Class { private: GPIO_TypeDef *BSP_DS18B20_PORT; uint16_t BSP_DS18B20_PIN; float *Temp_Sum; uint8_t LOCATION; public: DS18B20_Class(GPIO_TypeDef *BSP_DS18B20_PORT, uint16_t BSP_DS18B20_PIN, uint8_t LOCATION, float *Temp_data); void delay_us(uint16_t us); GPIO_PinState DS18B20_IN(void); void DS18B20_OUT_1(void); void DS18B20_OUT_0(void); void DS18B20_Mode_OUT_PP(void); void DS18B20_Mode_IN_NP(void); void DS18B20_Reset(void); uint8_t DS18B20_Presence(void); uint8_t DS18B20_ReadBit(void); uint8_t DS18B20_ReadByte(void); void DS18B20_WriteByte(uint8_t dat); void DS18B20_ReadId(uint8_t *ds18b20_id); void DS18B20_SkipRom(void); void DS18B20_MatchRom(void); uint8_t Init(void); float DS18B20_GetTemp_SkipRom(void); float DS18B20_GetTemp_MatchRom(uint8_t *ds18b20_id); void Start_Convert(void); void Get_Data(uint8_t ds18b20_id[64][8]); }; extern "C" { void User_DS18B20_Init(void); void User_Start_Convert(void); void User_Get_Data(void); } #endif 源文件: /* * @Author : fan-pengfei 2253770787@qq.com * @Date : 2023-03-07 13:43:26 * @LastEditors : fan-pengfei 2253770787@qq.com * @LastEditTime : 2023-03-07 16:41:42 * @FilePath : \Core\Src\ds18b20.cpp * @Description : * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. */ #include "ds18b20.h" #include "main.h" #include "DS18B20_ID.h" extern float Temp_Sum[64]; void DS18B20_Class::delay_us(uint16_t us) { tim_delay_us(us); // uint32_t delay; // delay = (1600 * us); // while (delay--) // { // } } GPIO_PinState DS18B20_Class::DS18B20_IN(void) { return HAL_GPIO_ReadPin(BSP_DS18B20_PORT, BSP_DS18B20_PIN); } void DS18B20_Class::DS18B20_OUT_1(void) { HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_SET); } void DS18B20_Class::DS18B20_OUT_0(void) { HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_RESET); } /** * @brief DS18B20 输出模式 */ void DS18B20_Class::DS18B20_Mode_OUT_PP(void) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = BSP_DS18B20_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(BSP_DS18B20_PORT, &GPIO_InitStruct); } /** * @brief DS18B20 输入模式 */ void DS18B20_Class::DS18B20_Mode_IN_NP(void) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = BSP_DS18B20_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(BSP_DS18B20_PORT, &GPIO_InitStruct); } /** * @brief 主机给从机发送复位脉冲 */ void DS18B20_Class::DS18B20_Reset(void) { DS18B20_Mode_OUT_PP(); // 主机输出 DS18B20_OUT_0(); // 主机至少产生 480us 的低电平复位信号 delay_us(750); DS18B20_OUT_1(); // 主机在产生复位信号后,需将总线拉高 // 从机接收到主机的复位信号后,会在 15 ~ 60 us 后给主机发一个存在脉冲 delay_us(15); } /** * @brief 检测从机给主机返回的存在脉冲 * @return 0:成功 1:失败 */ uint8_t DS18B20_Class::DS18B20_Presence(void) { uint8_t pulse_time = 0; DS18B20_Mode_IN_NP(); // 主机设为输入 while (DS18B20_IN() && (pulse_time = 100) { return 1; } else { pulse_time = 0; } while (!(DS18B20_IN()) && pulse_time = 240) { return 1; } else { return 0; } } /** * @brief 从DS18B20读取一个bit */ uint8_t DS18B20_Class::DS18B20_ReadBit(void) { uint8_t dat; DS18B20_Mode_OUT_PP(); // 读 0 和读 1 的时间至少要大于 60 us DS18B20_OUT_0(); // 读时间的起始:必须由主机产生 > 1us > 1; // 写 0 和写 1 的时间至少要大于60us if (testb) // 当前位写 1 { DS18B20_OUT_0(); delay_us(5); // 拉低发送写时段信号 DS18B20_OUT_1(); // 读取电平时间保持高电平 delay_us(65); } else // 当前位写 0 { DS18B20_OUT_0(); // 拉低发送写时段信号 delay_us(70); // 读取电平时间保持低电平 DS18B20_OUT_1(); delay_us(2); // 恢复时间 } } } /** * @brief 跳过匹配 DS18B20 ROM */ void DS18B20_Class::DS18B20_SkipRom(void) { DS18B20_Reset(); DS18B20_Presence(); DS18B20_WriteByte(0XCC); /* 跳过 ROM */ } /** * @brief 执行匹配 DS18B20 ROM */ void DS18B20_Class::DS18B20_MatchRom(void) { DS18B20_Reset(); DS18B20_Presence(); DS18B20_WriteByte(0X55); /* 匹配 ROM */ } DS18B20_Class::DS18B20_Class(GPIO_TypeDef *BSP_DS18B20_PORT, uint16_t BSP_DS18B20_PIN, uint8_t LOCATION, float *Temp_data) { DS18B20_Class::BSP_DS18B20_PORT = BSP_DS18B20_PORT; DS18B20_Class::BSP_DS18B20_PIN = BSP_DS18B20_PIN; DS18B20_Class::LOCATION = LOCATION; DS18B20_Class::Temp_Sum = Temp_data; } uint8_t DS18B20_Class::Init(void) { DS18B20_Mode_OUT_PP(); DS18B20_OUT_1(); DS18B20_Reset(); return DS18B20_Presence(); } /** * 存储的温度是16 位的带符号扩展的二进制补码形式 * 当工作在12位分辨率时,其中5个符号位,7个整数位,4个小数位 * * |---------整数----------|-----小数 分辨率 1/(2^4)=0.0625----| * 低字节 | 2^3 | 2^2 | 2^1 | 2^0 | 2^(-1) | 2^(-2) | 2^(-3) | 2^(-4) | * * * |-----符号位:0->正 1->负-------|-----------整数-----------| * 高字节 | s | s | s | s | s | 2^6 | 2^5 | 2^4 | * * * 温度 = 符号位 + 整数 + 小数*0.0625 */ /** * @brief 在跳过匹配 ROM 情况下获取 DS18B20 温度值 * @param 无 * @retval 温度值 */ float DS18B20_Class::DS18B20_GetTemp_SkipRom(void) { uint8_t tpmsb, tplsb; int16_t s_tem; float f_tem; DS18B20_SkipRom(); DS18B20_WriteByte(0X44); /* 开始转换 */ DS18B20_SkipRom(); DS18B20_WriteByte(0XBE); /* 读温度值 */ tplsb = DS18B20_ReadByte(); tpmsb = DS18B20_ReadByte(); s_tem = tpmsb 要注意其中的`extern "C"`的用法; ### Main中调用: > 注意main文件也是cpp后缀; ```c++ void main(void) { User_DS18B20_Init(); while (1) { User_Start_Convert(); HAL_Delay(200); // 更新速率为200ms,等待转换结束 User_Get_Data(); int i = 0; while (i 使用C编写  使用C++编写  **C:** Total RO Size (Code + RO Data) 14016 (13.69kB) Total RW Size (RW Data + ZI Data) 2040 (1.99kB) Total ROM Size (Code + RO Data + RW Data) 14464 ( 14.13kB) **C++:** Total RO Size (Code + RO Data) 11780 (11.50kB) Total RW Size (RW Data + ZI Data) 2864 (2.80kB) Total ROM Size (Code + RO Data + RW Data) 12220 ( 11.93kB) > 相比较而言C用的ROM比较多一些,C++用的RAM比较多一些; **可见在某些情况下,使用C++编写代码可以有效缩减代码体积,且代码更易懂;** ### microlib 使用C++编译的话,就没法再使用MicroLIB,因为MicroLIB为非标准的精简库,会与标准C++产生冲突; ### 中断服务程序 如果中断服务程序是异常的,因为stm32的中断入口矢量是按C的方式进入的,因此需要在整个文档的头部和末尾加上extern “C”{}用大括号把整个代码段扩住,这样中断就可以正常的进入了; ### Cubemx Cubemx在生成头文件中已经加入了: ```c #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif 所以在不是其生成的文件中要注意extern "C"的使用; ...