首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的设备驱动抽象层

C++中的设备驱动抽象层
EN

Code Review用户
提问于 2022-02-17 17:37:24
回答 1查看 248关注 0票数 3

首先,我在C++方面的知识非常有限,但我在C方面有很好的背景。

我目前正在为STM32单片机编写使用C++的固件。我必须编写一个驱动程序,以便使用SPI接口与IC通信。集成电路有许多寄存器。寄存器就像内存一样被访问,每个寄存器都是可寻址的。与所有单元格都具有相同大小的内存不同,有些寄存器比其他寄存器大。大多数集成电路都以同样的方式工作。由于这个原因,我正在尝试使驱动程序抽象,这样它就可以被重用,而且几乎没有什么变化,也可以与其他的I甚至通信接口一起使用。我也在寻找简单易用的方法。我想写到寄存器中,就像写入变量和从寄存器读取一样简单,就像从变量中读取一样简单。我想出的代码按需要工作,但我对实现不太确定。我的一些关注被写成代码中的注释(例如Info成员不是私有的)。我主要关心的是每个寄存器创建一个新的模板实现。代码大小将随着一个IC拥有的寄存器的数量而增加。由于MCU的内存非常有限,代码大小可能会出现问题。我确信有一种方法可以为每个寄存器创建一个模板实现,因为所有寄存器都包含一个Info字段。读写函数不关心寄存器的结构,每个寄存器都可以看作是一个字节数组。因此,读写功能只需要Info字段知道地址和大小。

代码语言:javascript
复制
#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;
}
EN

回答 1

Code Review用户

发布于 2022-02-17 19:21:07

这个想法是好的,但它可以改进。首先,将寄存器类型与其地址分离。例如,考虑到一个MCU可能有多个UART实例,并且您希望只声明一次struct UART,然后在不同的地址使用它。很高兴能写到:

代码语言:javascript
复制
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的成员变量:

代码语言:javascript
复制
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);
};

更好的是,后者还允许您使用数组表示法声明相同类型的多个寄存器:

代码语言:javascript
复制
Driver<UART> UARTs[2] = {0x00, 0x10};

其他一些改进:

  • Driver可以使用sizeof(T)获取寄存器的大小,因此不需要显式地将其放入Info变量或将其作为(模板)参数传递。
  • operator=()应该以一个const引用作为输入,并返回*this
  • 使用C++形式的#includes:<cstdint><cstdio>
  • 如果可能的话,用C++'s std::cout代替C的printf()要安全得多。如果可以的话,可以使用C++17's std::format来获得这两个世界的最佳效果。
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/274208

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档