我试图实现这种访问寄存器的方法: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;
}
答案 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。子节点之间的差异是硬件寄存器的地址。