首先,我在C++方面的知识非常有限,但我在C方面有很好的背景。
我目前正在为STM32单片机编写使用C++的固件。我必须编写一个驱动程序,以便使用SPI接口与IC通信。集成电路有许多寄存器。寄存器就像内存一样被访问,每个寄存器都是可寻址的。与所有单元格都具有相同大小的内存不同,有些寄存器比其他寄存器大。大多数集成电路都以同样的方式工作。由于这个原因,我正在尝试使驱动程序抽象,这样它就可以被重用,而且几乎没有什么变化,也可以与其他的I甚至通信接口一起使用。我也在寻找简单易用的方法。我想写到寄存器中,就像写入变量和从寄存器读取一样简单,就像从变量中读取一样简单。我想出的代码按需要工作,但我对实现不太确定。我的一些关注被写成代码中的注释(例如Info成员不是私有的)。我主要关心的是每个寄存器创建一个新的模板实现。代码大小将随着一个IC拥有的寄存器的数量而增加。由于MCU的内存非常有限,代码大小可能会出现问题。我确信有一种方法可以为每个寄存器创建一个模板实现,因为所有寄存器都包含一个Info字段。读写函数不关心寄存器的结构,每个寄存器都可以看作是一个字节数组。因此,读写功能只需要Info字段知道地址和大小。
#include <stdint.h>
#include <stdio.h>
struct Reg_t
{
uint8_t Address;
uint8_t Size;
};
struct RegA_t
{
uint8_t Field1 : 1;
uint8_t Field2 : 3;
uint8_t Field3 : 4;
// Would like Info to be private but cannot
// Only Driver class need access
//private:
// Cannot use inheritance because of static const
// Different registers must not share the same Address and Size in memory
static const Reg_t Info;
};
struct RegB_t
{
uint32_t Field1 : 6;
uint32_t Field2 : 12;
uint32_t Field3 : 14;
// Would like Info to be private but cannot
// Only Driver class need access
//private:
// Cannot use inheritance because of static const
// Different registers must not share the same Address and Size in memory
static const Reg_t Info;
};
const Reg_t RegA_t::Info = {0x00, sizeof(RegA_t)};
const Reg_t RegB_t::Info = {0x01, sizeof(RegB_t)};
class Device
{
template <typename T>
class Driver
{
public:
T& operator=(T& buf)
{
// Implement code to write to the device
printf("Writing to the device...\n");
printf("Register address: %u, size: %u\n", buf.Info.Address, buf.Info.Size);
return buf;
}
operator T() const
{
T buf = {0};
// Implement code to read from the device
printf("Reading from the device...\n");
printf("Register address: %u, size: %u\n", buf.Info.Address, buf.Info.Size);
return buf;
}
};
public:
Driver<RegA_t> RegA;
Driver<RegB_t> RegB;
};
int main()
{
Device myDevice;
RegA_t RegA;
RegB_t RegB;
// Reading the register
RegA = myDevice.RegA;
// Editing the content
RegA.Field1 = 1;
RegA.Field2 = 2;
RegA.Field3 = 3;
// Updating the content
myDevice.RegA = RegA;
printf("\n");
// Reading the register
RegB = myDevice.RegB;
// Editing the content
RegB.Field1 = 1;
RegB.Field2 = 2;
RegB.Field3 = 3;
// Updating the content
myDevice.RegB = RegB;
return 0;
}发布于 2022-02-17 19:21:07
这个想法是好的,但它可以改进。首先,将寄存器类型与其地址分离。例如,考虑到一个MCU可能有多个UART实例,并且您希望只声明一次struct UART,然后在不同的地址使用它。很高兴能写到:
class Device {
template<typename T, uint8_t Address>
struct Driver {
T& operator=(const T& buf) {
std::cout << "Writing to the device...\n"
<< "Register address: " << Address
<< ", size: " << sizeof(T) << '\n';
return *this;
}
...
};
public:
Driver<UART, 0x00> UART1;
Driver<UART, 0x10> UART2;
};但是请注意,通过使类型的地址部分(无论是通过Info变量还是作为Driver的模板参数),上述示例中的每个UART都将得到自己的、不同的Driver模板实例化。这可能会导致机器代码的数量超过必要。考虑将地址仅作为Driver的成员变量:
class Device {
template<typename T>
struct Driver {
Driver(uint8_t address): address(address) {}
T& operator=(const T& buf) {
std::cout << "Writing to the device...\n"
<< "Register address: " << address
<< ", size: " << sizeof(T) << '\n';
return *this;
}
...
private:
uint8_t address;
};
public:
Driver<UART> UART1(0x00);
Driver<UART> UART2(0x10);
};更好的是,后者还允许您使用数组表示法声明相同类型的多个寄存器:
Driver<UART> UARTs[2] = {0x00, 0x10};其他一些改进:
Driver可以使用sizeof(T)获取寄存器的大小,因此不需要显式地将其放入Info变量或将其作为(模板)参数传递。operator=()应该以一个const引用作为输入,并返回*this。#includes:<cstdint>和<cstdio>std::cout代替C的printf()要安全得多。如果可以的话,可以使用C++17's std::format来获得这两个世界的最佳效果。https://codereview.stackexchange.com/questions/274208
复制相似问题