嵌套类实现GPS-Arduino I2C通信的编译问题及方案咨询
问题分析与解决方案
你的代码存在两个核心问题:
- 内部类(
NMEA、UBX_COMMS等)没有持有外部类UBLOX_INTERFACE的实例引用,无法直接访问其非静态成员i2cAddress; - 你在
UBLOX_INTERFACE构造函数中创建的内部类对象是局部变量,构造函数执行结束后就会销毁,外部代码无法通过u.ubxCFG_RST这类方式访问,和你的期望用法矛盾。
以下是具体的修正方案:
1. 修改头文件:调整内部类结构与成员定义
//UBLOX.h #include <Wire.h> class UBLOX_INTERFACE { private: const uint8_t i2cAddress; public: UBLOX_INTERFACE(uint8_t address); // NMEA内部类:持有外部类引用以访问i2cAddress class NMEA { private: UBLOX_INTERFACE& parent; void intakeNMEA(); public: NMEA(UBLOX_INTERFACE& p) : parent(p) {} }; // UBX通信基类:持有外部类引用 class UBX_COMMS { private: UBLOX_INTERFACE& parent; uint8_t classByte; uint8_t idByte; uint8_t payloadLength[2]; uint16_t stdPacketLength; uint16_t packetLengthCalc(uint8_t l0, uint8_t l1) { return l0 + (l1 << 8) + 8; } void calcAndInsertUBXChecksum(uint8_t packet[], const uint16_t packetLength); uint8_t prepAndSendPacket(uint8_t packet[], const uint16_t packetLength); public: UBX_COMMS(UBLOX_INTERFACE& p, uint8_t ubxclass, uint8_t id, uint8_t length0, uint8_t length1) : parent(p), classByte(ubxclass), idByte(id), payloadLength{length0, length1}, stdPacketLength(packetLengthCalc(length0, length1)) {} }; // UBX配置速率子类:封装固定命令参数 class UBX_CFG_RATE : public UBX_COMMS { public: UBX_CFG_RATE(UBLOX_INTERFACE& p) : UBX_COMMS(p, 0x06, 0x08, 0, 6) {} bool set(const uint16_t GNSSmeasureRate); void poll(); }; // UBX重置命令子类:封装固定命令参数 class UBX_CFG_RST : public UBX_COMMS { public: UBX_CFG_RST(UBLOX_INTERFACE& p) : UBX_COMMS(p, 0x06, 0x04, 0, 4) {} bool gnssReset(const uint8_t navBbrMask); bool hardwareReset(const uint8_t resetMode); }; // 将内部类对象设为外部类成员,供外部直接访问 NMEA nmea; UBX_CFG_RATE ubxCFG_RATE; UBX_CFG_RST ubxCFG_RST; };
2. 修改CPP文件:实现构造函数与成员函数
//UBLOX.cpp #include "UBLOX.h" // 外部类构造函数:初始化内部类成员,传入自身引用 UBLOX_INTERFACE::UBLOX_INTERFACE(uint8_t address) : i2cAddress(address), nmea(*this), ubxCFG_RATE(*this), ubxCFG_RST(*this) { // 无需再创建局部内部类对象 } // NMEA类:通过parent访问外部类的i2cAddress void UBLOX_INTERFACE::NMEA::intakeNMEA() { Wire.beginTransmission(parent.i2cAddress); // 后续NMEA数据接收逻辑... } // UBX校验和计算实现 void UBLOX_INTERFACE::UBX_COMMS::calcAndInsertUBXChecksum(uint8_t packet[], const uint16_t packetLength) { uint8_t ck_a = 0, ck_b = 0; for (uint16_t i = 2; i < packetLength - 2; i++) { ck_a += packet[i]; ck_b += ck_a; } packet[packetLength - 2] = ck_a; packet[packetLength - 1] = ck_b; } // UBX包发送实现:通过parent访问i2cAddress uint8_t UBLOX_INTERFACE::UBX_COMMS::prepAndSendPacket(uint8_t packet[], const uint16_t packetLength) { calcAndInsertUBXChecksum(packet, packetLength); Wire.beginTransmission(parent.i2cAddress); Wire.write(packet, packetLength); return Wire.endTransmission(); } // 设置GNSS测量率示例 bool UBLOX_INTERFACE::UBX_CFG_RATE::set(const uint16_t GNSSmeasureRate) { uint8_t packet[10] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x06, (GNSSmeasureRate & 0xFF), (GNSSmeasureRate >> 8), 0x01, 0x00}; uint8_t err = prepAndSendPacket(packet, sizeof(packet)); return (err == 0); } // 硬件重置示例 bool UBLOX_INTERFACE::UBX_CFG_RST::hardwareReset(const uint8_t resetMode) { uint8_t packet[8] = {0xB5, 0x62, 0x06, 0x04, 0x00, 0x04, resetMode, 0x00}; uint8_t err = prepAndSendPacket(packet, sizeof(packet)); return (err == 0); } // 其他成员函数(poll、gnssReset)可按同理实现
3. 验证期望使用方式
现在你的代码可以按预期正常运行:
void setup() { Wire.begin(); UBLOX_INTERFACE u(0x42); // 传入GPS的I2C地址 u.ubxCFG_RST.hardwareReset(0x01); // 执行硬件重置 u.ubxCFG_RATE.set(1000); // 设置1秒GNSS测量率 } void loop() { u.nmea.intakeNMEA(); // 持续接收NMEA数据 delay(100); }
关键修改说明
- 内部类持有外部类引用:给每个需要访问
i2cAddress的内部类添加UBLOX_INTERFACE& parent成员,构造时传入外部类实例(*this),解决非静态成员访问问题; - 内部类对象作为外部类成员:把
nmea、ubxCFG_RATE等从局部变量改为UBLOX_INTERFACE的成员变量,确保外部代码可以直接访问; - 封装固定命令参数:给UBX命令子类(如
UBX_CFG_RATE)添加构造函数,把固定的命令参数(如0x06,0x08)写死在子类中,简化外部使用流程。
内容的提问来源于stack exchange,提问作者Brendan
相关产品推荐
相关产品推荐

