在C ++中注册Access

时间:2014-10-21 21:48:14

标签: c++ namespaces embedded

我试图实现这种访问寄存器的方法:http://www.drdobbs.com/cpp/register-access-in-c/184401954?pgno=2

这是一个示例代码:在这个例子中,我试图创建两个寄存器基--UART和PWM。两者都有它们的寄存器集(偏离基址)。 基地址被模拟用于测试目的。

问题1 :文章没有详细介绍处理多个设备的方法,是我的实施正确理解吗?如果是的话

问题2 有没有办法避免重复代码--regWrite,regRead和regAddress函数? 所以理想情况下,我想用基地址和枚举寄存器来定义名称空间并重用访问函数。实现这一目标的最佳方法是什么?

我在regWrite,regRead,regAddress外部命名空间中遇到编译错误。它无法识别寄存器,baseAddress。

注意:我正在为2k RAM控制器编写代码,并在其上运行RTOS。所以我也有兴趣保持代码大小和执行时间相对较低,但仍然提高了可读性。

namespace UART{
enum Registers {
    STATUS = 0, RESET = 1
};

unsigned int fakeBase[2];
unsigned int baseAddress = (unsigned int)(&fakeBase);

inline volatile unsigned int *regAddress(Registers Reg){
    return reinterpret_cast<volatile unsigned int *> (baseAddress + Reg);
}
inline void regWrite(Registers Reg, unsigned int value){
    *regAddress(Reg) = value;
}

inline volatile unsigned int regRead(Registers Reg){
    return *regAddress(Reg);
}
}


namespace PWM{
enum Registers {
    DCA = 0, DCB = 1, DCC = 2
};

unsigned int fakeBase[3];
unsigned int baseAddress = (unsigned int)(&fakeBase);

inline volatile unsigned int *regAddress(Registers Reg){
    return reinterpret_cast<volatile unsigned int *> (baseAddress + Reg);
}
inline void regWrite(Registers Reg, unsigned int value){
    *regAddress(Reg) = value;
}

inline volatile unsigned int regRead(Registers Reg){
    return *regAddress(Reg);
}
}


int main() {
    regWrite(UART::STATUS, 12);
    std::cout << "UART 0 " << regRead(UART::STATUS)<< std::endl;
    regWrite(PWM::DCA, 34);
    std::cout << "PWM 0 " << regRead(PWM::DCA)<< std::endl;
    regWrite(UART::RESET, 13);
    std::cout << "UART 1 " << regRead(UART::RESET)<< std::endl;
    return 0;
}

2 个答案:

答案 0 :(得分:2)

实施,作为玩具&#34;将工作。当然,任何现代操作系统都禁止用户模式应用程序访问真实硬件,你需要编写驱动程序代码,我认为除非你使用的是Windows CE或Windows Phone,否则你无法用C ++编写驱动程序。 Windows或Linux(尽管两者都使用&#34;继承风格&#34;接口实现驱动程序)。

为了减少代码重复,您可以实现通用寄存器访问的基类,这些内容是这样的:并使用类而不是名称空间来实现区别。

template<typename T> class RegisterBase
{
 public: 
   RegisterBase(T *location, size_t count) : 
      baseAddress(location), numRegisters(count)
   {}

  inline volatile T *regAddress(unsiged int Reg) {
    assert(Reg < numRegisters);
    return reinterpret_cast<volatile unsigned int *> (baseAddress + Reg);
  }
  inline void regWrite(unsigned int Reg, T value) {
    *regAddress(Reg)=value;
  }
  inline volatile T regRead(unsigned int Reg) {
    return *regAddress(Reg);
  }
private:
  T* baseAddress;
  unsigned int numRegisters;
};


class UART : public RegisterBase<unsigned int>
{
public:
  enum Registers {
    STATUS=0, RESET=1, 
    NUMREGS = 2
  };

  UART() : RegisterBase(&fakeBase, NUMREGS) {}

private:
   unsigned int fakeBase[NUMREGS];
};


class PWM : public RegisterBase<unsigned short>
{
  enum Registers {
    DCA=0, DCB=1, DCC=2,
    NUMREGS = 3
  };

  PWM() : RegisterBase(&fakeBase, NUMREGS) {}

private:
   unsigned int fakeBase[NUMREGS];
};

您现在必须&#34;实例化&#34;你的注册集:

PWM pwm;
UART uart1, uart2; 

uart1.RegWrite(UART::Status, 12); 
pwm.RegWrite(PWM::DCB, 42); 

答案 1 :(得分:1)

有许多方法可以为硬件设备建模。 一种方法是使用所有寄存器定义结构,然后维护指向设备的全局指针:

struct UART_Registers
{
  uint32_t status;
  uint32_t transmit_register;
  uint32_t receive_register;
};

UART_Registers volatile * const p_UART0 = (UART_Registers *) 0xFFFFD000; // The address of the UART registers.

//...
  uint32_t transmit_value = (uint32_t)'X';
  p_UART0->transmit_register = transmit_value;

另一个可能是指向寄存器的指针结构:

struct UART_Pointers
{
  uint32_t volatile * const status;
  uint32_t volatile * const transmit;
  uint32_t volatile * const receive;
};
UART_Pointers UART_0 =
{
  0xFFFFD000, // status
  0xFFFFD004, // transmit
  0xFFFFD008, // receive;
};

//...
  uint32_t receive = *UART_0.receive;

我见过的第三个版本将每个寄存器的指针声明为宏:

#define UART_BASE_ADDR 0xFFFFD000
#define REGISTER_UART_0_STATUS (UART_BASE_ADDR + 0U)
#define REGISTER_UART_0_TRANSMIT (UART_BASE_ADDR + 4)
#define REGISTER_UART_0_RECEIVE  (UART_BASE_ADDR + 8)
//...
uint32_t status = *REGISTER_UART_0_STATUS;

这都是意见。

我对可读性的偏好是第一个。

编辑1:多个设备
存在片上系统(SOC),其具有硬件设备的多个“实例”。例如,SOC可以有3个UARTS。

对于这种情况,我创建了一个UART_Base类,它定义了UART的所有通用性。子类负责特定地址。例如,UART0可能位于0xFFFFD000,UART1位于0xFFFFD100,UART2位于0xFFFFD200。子节点之间的差异是硬件寄存器的地址。