第十六周项目三:max带来的冲突

本文分析了一个关于函数模板冲突的问题,通过具体示例代码展示了如何区分标准库中的max函数与自定义的max函数模板,避免编译错误。

问题及代码:

/*
*Copyright (c)2015,烟台大学计算机与控制工程学院
*All rights reserved.
*文件名称: max带来的冲突  .cpp
*作    者:白云飞
*完成日期:2015年6月17日
*版 本 号:v1.0
*
*问题描述:分析下面程序出现的编译错误,给出解决的方案。
*/
#include<iostream>
using namespace std;
//定义函数模板
template<class T>
T max(T a, T b)
{
    return (a>b)?a:b;
}

int main()
{
    int x=2,y=6;
    double x1=9.123,y1=12.6543;
    cout<<"把T实例化为int:"<<std::max(x,y)<<endl;
    cout<<"把T实例化为double:"<<std::max(x1,y1)<<endl;
    return 0;
}


运行结果:

 

学习心得:

明确调用的是当前工作域中定义的max,所以要在max前加"std::"或者"::",也可以去掉using namespace std;,在main中cout等std中的名称前加std::,于是,main中调用的max,是本工作域中定义的函数模板。

当然可以!我们来 **非常详细地讲解** 第步: --- ## ✅ 目标:修改 `max30102.h` 和相关底层代码,使其适配你当前的 I²C 引脚与通信方式 你目前使用的是 **GPIO 模拟 I²C(软件 I²C)**,SCL 和 SDA 分别连接在 STM32 的某个 IO 上(比如 PB8 和 PB9),并且你已经有现成的 OLED 驱动中实现了 I²C 通信函数。 现在要让 MAX30102 芯片也能通过同样的方式通信 —— **不使用硬件 I²C,而是复用你的模拟 I²C 函数。** --- # 🧩 一、确认你的 I²C 硬件连接 假设你的接线如下(常见情况): | MAX30102 引脚 | 连接到 MCU | |---------------|-----------| | SCL | PB6 | | SDA | PB7 | | INT | PA1 | | VCC/GND | 3.3V / GND | > ⚠️ 注意:不要和 OLED 共用同一组引脚!否则会冲突! --- # 🔧 二、准备工作:确保已有可用的“通用模拟 I²C”函数 你在 `OLED.c` 中已经写了这些函数(江协科技风格): ```c void OLED_W_SCL(uint8_t BitValue); void OLED_W_SDA(uint8_t BitValue); void OLED_I2C_Start(void); void OLED_I2C_Stop(void); void OLED_I2C_SendByte(uint8_t Byte); ``` 但它们被命名为 `OLED_...`,说明是专用于 OLED 的。 👉 我们的目标是:**把这些函数抽象出来,变成“通用软件 I²C”函数,供 MAX30102 复用!** --- # ✅ 第一步:创建一个通用的软件 I²C 驱动模块(推荐做法) ### 📁 新建两个文件: - `soft_i2c.h` - `soft_i2c.c` --- ### ✅ `soft_i2c.h` —— 定义接口 ```c // soft_i2c.h #ifndef __SOFT_I2C_H #define __SOFT_I2C_H #include "stm32f10x.h" // ------------------ 用户可配置区 ------------------ // 设置 MAX30102 使用的 SCL 和 SDA 引脚 #define SOFT_I2C_SCL_PIN GPIO_Pin_6 #define SOFT_I2C_SDA_PIN GPIO_Pin_7 #define SOFT_I2C_PORT GPIOB // ------------------------------------------------- // 初始化函数 void Soft_I2C_Init(void); // 基础操作函数 void Soft_I2C_Start(void); void Soft_I2C_Stop(void); void Soft_I2C_SendByte(uint8_t byte); uint8_t Soft_I2C_RecvByte(uint8_t ack); // ack=1 发送ACK, ack=0 发送NACK uint8_t Soft_I2C_WaitAck(void); void Soft_I2C_Ack(void); void Soft_I2C_NAck(void); #endif ``` --- ### ✅ `soft_i2c.c` —— 实现通用模拟 I²C ```c // soft_i2c.c #include "soft_i2c.h" #include "Delay.h" // 必须包含延时函数 // 写 SCL 引脚电平 static void Soft_I2C_W_SCL(uint8_t bit) { if (bit) { GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN); } else { GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN); } } // 写 SDA 引脚电平 static void Soft_I2C_W_SDA(uint8_t bit) { GPIO_InitTypeDef GPIO_InitStructure; // 配置为推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = SOFT_I2C_SDA_PIN; GPIO_Init(SOFT_I2C_PORT, &GPIO_InitStructure); if (bit) { GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN); } else { GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN); } } // 读 SDA 引脚电平(需设为输入) static uint8_t Soft_I2C_R_SDA(void) { uint8_t value; GPIO_InitTypeDef GPIO_InitStructure; // 配置为上拉输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = SOFT_I2C_SDA_PIN; GPIO_Init(SOFT_I2C_PORT, &GPIO_InitStructure); value = GPIO_ReadInputDataBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN); return value; } // -------------------- I2C 协议实现 -------------------- void Soft_I2C_Start(void) { Soft_I2C_W_SDA(1); Soft_I2C_W_SCL(1); Delay_us(4); Soft_I2C_W_SDA(0); Delay_us(4); Soft_I2C_W_SCL(0); } void Soft_I2C_Stop(void) { Soft_I2C_W_SCL(0); Soft_I2C_W_SDA(0); Delay_us(4); Soft_I2C_W_SCL(1); Delay_us(4); Soft_I2C_W_SDA(1); Delay_us(4); } uint8_t Soft_I2C_WaitAck(void) { uint8_t timeout = 0; Soft_I2C_W_SDA(1); // 释放SDA Delay_us(1); Soft_I2C_W_SCL(1); Delay_us(1); while (Soft_I2C_R_SDA()) { timeout++; if (timeout > 100) { // 超时判断 Soft_I2C_Stop(); return 1; // 无应答 } } Soft_I2C_W_SCL(0); return 0; // 收到 ACK } void Soft_I2C_Ack(void) { Soft_I2C_W_SCL(0); Soft_I2C_W_SDA(0); Delay_us(2); Soft_I2C_W_SCL(1); Delay_us(2); Soft_I2C_W_SCL(0); } void Soft_I2C_NAck(void) { Soft_I2C_W_SCL(0); Soft_I2C_W_SDA(1); Delay_us(2); Soft_I2C_W_SCL(1); Delay_us(2); Soft_I2C_W_SCL(0); } void Soft_I2C_SendByte(uint8_t byte) { for (int i = 0; i < 8; i++) { Soft_I2C_W_SCL(0); if ((byte << i) & 0x80) { Soft_I2C_W_SDA(1); } else { Soft_I2C_W_SDA(0); } Delay_us(2); Soft_I2C_W_SCL(1); Delay_us(2); } Soft_I2C_W_SCL(0); } uint8_t Soft_I2C_RecvByte(uint8_t ack) { uint8_t byte = 0; Soft_I2C_W_SDA(1); // 释放总线,准备接收 for (int i = 0; i < 8; i++) { Soft_I2C_W_SCL(0); Delay_us(2); Soft_I2C_W_SCL(1); Delay_us(1); byte <<= 1; if (Soft_I2C_R_SDA()) byte |= 1; Delay_us(1); } Soft_I2C_W_SCL(0); if (ack) { Soft_I2C_Ack(); } else { Soft_I2C_NAck(); } return byte; } // 初始化 SCL/SDA 引脚 void Soft_I2C_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); Soft_I2C_W_SCL(1); Soft_I2C_W_SDA(1); } ``` --- # ✅ 第二步:修改 `max30102.h`,声明对外 API ```c // max30102.h #ifndef __MAX30102_H #define __MAX30102_H #include "stm32f10x.h" // MAX30102 寄存器地址 #define REG_INTR_STATUS_1 0x00 #define REG_INTR_STATUS_2 0x01 #define REG_INTR_ENABLE_1 0x02 #define REG_FIFO_WR_PTR 0x04 #define REG_OVF_COUNTER 0x05 #define REG_FIFO_RD_PTR 0x06 #define REG_FIFO_DATA 0x07 #define REG_FIFO_CONFIG 0x08 #define REG_MODE_CONFIG 0x09 #define REG_SPO2_CONFIG 0x0A #define REG_LED1_PA 0x0C #define REG_LED2_PA 0x0D #define REG_PILOT_PA 0x10 // I2C 地址(ADDR 接 GND → 0x57;读地址 +1) #define MAX30102_WRITE_ADDR 0xAE #define MAX30102_READ_ADDR 0xAF // 外部调用接口 void MAX30102_Init(void); void MAX30102_Reset(void); void maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led); #endif ``` --- # ✅ 第步:修改 `max30102.c`,使用新的 `soft_i2c.c` 删除原来的底层 I²C 函数(如 `MAX30102_IIC_Start` 等),改用我们刚刚写的 `Soft_I2C_xxx()`。 ### 示例:重写 `maxim_max30102_write_reg` ```c // max30102.c #include "max30102.h" #include "soft_i2c.h" void maxim_max30102_write_reg(uint8_t reg_addr, uint8_t data) { Soft_I2C_Start(); Soft_I2C_SendByte(MAX30102_WRITE_ADDR); Soft_I2C_WaitAck(); Soft_I2C_SendByte(reg_addr); Soft_I2C_WaitAck(); Soft_I2C_SendByte(data); Soft_I2C_WaitAck(); Soft_I2C_Stop(); } ``` ### 示例:读取 FIFO 数据 ```c void maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led) { uint8_t temp[6]; Soft_I2C_Start(); Soft_I2C_SendByte(MAX30102_WRITE_ADDR); Soft_I2C_WaitAck(); Soft_I2C_SendByte(REG_FIFO_DATA); Soft_I2C_WaitAck(); Soft_I2C_Start(); // Repeated Start Soft_I2C_SendByte(MAX30102_READ_ADDR); Soft_I2C_WaitAck(); // 读6字节 temp[0] = Soft_I2C_RecvByte(1); temp[1] = Soft_I2C_RecvByte(1); temp[2] = Soft_I2C_RecvByte(1); temp[3] = Soft_I2C_RecvByte(1); temp[4] = Soft_I2C_RecvByte(1); temp[5] = Soft_I2C_RecvByte(0); // 最后一个 NACK Soft_I2C_Stop(); // 合并为24位数据 *pun_red_led = ((uint32_t)temp[0] << 16) | ((uint32_t)temp[1] << 8) | temp[2]; *pun_ir_led = ((uint32_t)temp[3] << 16) | ((uint32_t)temp[4] << 8) | temp[5]; // 清除状态寄存器(可选) maxim_max30102_write_reg(REG_INTR_STATUS_1, 0xFF); maxim_max30102_write_reg(REG_INTR_STATUS_2, 0xFF); } ``` ### 初始化函数 ```c void MAX30102_Init(void) { Soft_I2C_Init(); // 初始化模拟 I²C 引脚 // 复位芯片 maxim_max30102_write_reg(REG_MODE_CONFIG, 0x40); Delay_ms(1); maxim_max30102_write_reg(REG_MODE_CONFIG, 0x03); // SpO2 mode maxim_max30102_write_reg(REG_SPO2_CONFIG, 0x27); // 100Hz, 400us pulse width maxim_max30102_write_reg(REG_LED1_PA, 0x24); // LED1电流 maxim_max30102_write_reg(REG_LED2_PA, 0x24); // LED2电流 maxim_max30102_write_reg(REG_FIFO_CONFIG, 0x0F); // 每样本24位,FIFO Almost Full = 17 } ``` --- # ✅ 第四步:主控板中断引脚处理(INT) 如果你把 MAX30102 的 `INT` 引脚接到 `PA1`,可以在 `menu.c` 或定时器里这样检测是否有新数据: ```c // 判断是否收到中断(有新数据) #define MAX30102_INT (!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)) // 下降沿有效 ``` 然后在采集时等待: ```c while (!MAX30102_INT); // 等待新数据 maxim_max30102_read_fifo(&red, &ir); ``` --- # ✅ 总结:你现在完成了什么? | 功能 | 是否完成 | |------|----------| | 创建独立的 `soft_i2c.c/.h` 模块 | ✅ | | 将 MAX30102 底层 I²C 替换为你自己的模拟 I²C | ✅ | | 不再依赖对方的 `delay.c` 或 `oled.c` | ✅ | | 所有通信走你定义的引脚(PB6/PB7) | ✅ | | 可与其他设备共用软件 I²C 总线(只要不同地址) | ✅ | --- ## ✅ 最终工程结构建议 ``` Project/ ├── Core/ │ ├── main.c │ └── ... ├── HardWare/ │ ├── OLED.c/.h │ ├── Key.c/.h │ ├── soft_i2c.c/.h ← 新增 │ ├── max30102.c/.h ← 修改过 │ ├── algorithm.c/.h ← Maxim算法 │ └── heart_rate_spo2.c/.h ← 你的UI封装 └── User/ └── menu.c ← 调用 Show_MAX30102_Page() ``` --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值