用于I / O引脚抽象的C ++类


13

我正在寻找硬件I / O点或引脚的C ++抽象。诸如in_pin,out_pin,inout_pin或open_collector_pin之类的东西。

我当然可以自己提出这样的一组抽象,所以我不是在寻找“嘿,您可能会用这种方式”类型的答案,而是在“看一下在此库中使用的该库”。这个项目'。

Google没做任何事情,也许是因为我不知道其他人怎么称呼它。

我的目标是建立基于这些点的I / O库,但也要提供这些点,因此例如将HD44780 LCd连接到芯片的IO引脚或I2C(或SPI)将很容易I / O扩展器或可以通过其他方式控制的任何其他点,而无需更改LCD类。

我知道这是电子/软件方面的问题,很抱歉,如果它不属于这里。

@leon:接线这是一大包软件,我需要仔细看。但是似乎他们没有像我想要的那样使用pin抽象。例如在键盘实现中,我看到

digitalWrite(columnPins[c], LOW);   // Activate the current column.

这意味着存在一个知道如何写入I / O引脚的函数(digitalWrite)。这使得在不重写digitalWrite函数的情况下,无法添加新型的I / O引脚(例如,MCP23017上的一个,因此必须通过I2C对其进行写入)。

@Oli:我在Arduino IO示例中进行了搜索,但是似乎使用了与Wiring库相同的方法:

int ledPin = 13;                 // LED connected to digital pin 13
void setup(){
    pinMode(ledPin, OUTPUT);      // sets the digital pin as output
}

我们在这里谈论什么微控制器?
Majenko 2011年

那是无关紧要的。对于特定的微控制器,该uC的io引脚将实现适当的接口。但这是针对C ++的,因此请考虑使用ARM,Cortex和MIPS等32位芯片。
Wouter van Ooijen 2011年

1
我从没用过,但是Arduino不会像这样抽象所有的引脚吗?您可能会(也可能不会)获得一些有用的信息,以了解他们的工作方式。
奥利·格拉泽

1
至于重写digitalWrite函数,请看C ++中的“重载”。不久前,我为Arduino的IO扩展板编写了重载的digitalWrite函数。只要您使用不同的参数(我用“结构”替换了第一个“ int”),它就会优先选择您的digitalWrite而不是默认参数。
Majenko'9

1
我在柏林与C ++会谈时做了关于我在该主题上的工作的演讲。可以在youtube上找到:youtube.com/watch? v= k8sRQMx2qUw从那时起,我改用一种略有不同的方法,但是这次演讲可能仍然很有趣。
Wouter van Ooijen 2015年

Answers:


3

简短的回答:遗憾的是,没有图书馆可以做您想要的事情。我自己做过很多次,但总是在非开源项目中做过。我正在考虑在github上放一些东西,但我不确定何时可以。

为什么要使用C ++?

  1. 编译器可以自由使用动态字长表达式评估。C传播到int。您的字节掩码/移位可以更快或更小。
  2. 内联。
  3. 模板化操作使您可以使用类型安全性来更改字长和其他属性。

5

请允许我无耻地插入我的开源项目https://Kvasir.io。Kvasir :: Io部分提供了引脚操纵功能。您必须首先使用Kvasir :: Io :: PinLocation定义您的密码,如下所示:

constexpr PinLocation<0,4> led1;    //port 0 pin 4
constexpr PinLOcation<0,8> led2;

注意,由于这些是constexpr变量,因此实际上并没有使用RAM。

在整个代码中,您可以在“动作工厂”功能中使用这些引脚位置,例如makeOpenDrain,set,clear,makeOutput等。“动作工厂”实际上并不执行动作,而是返回可以使用Kvasir :: Register :: apply()执行的Kvasir :: Register :: Action。原因是apply()合并了传递给它的动作,当它们作用于一个相同的寄存器时,这样可以提高效率。

apply(makeOutput(led1),
    makeOutput(led2),
    makeOpenDrain(led1),
    makeOpenDrain(led2));

由于动作的创建和合并是在编译时完成的,因此产生的汇编代码应与典型的手工编码等效代码相同:

PORT0DIR |= (1<<4) | (1<<8);
PORT0OD |= (1<<4) | (1<<8);


2

在C ++中,可以编写一个类,以便您可以将I / O端口当作变量使用,例如

  PORTB = 0x12; / *写入8位端口* /
  如果(RB3)LATB4 = 1; / *读取一个I / O位,并有条件地写入另一个* /

不考虑基础实施。例如,如果使用的硬件平台不支持位级操作,但支持字节级寄存器操作,则可以(可能借助某些宏)使用内联读写定义静态类IO_PORTS属性bbRB3和bbLATB4,这样上面的最后一条语句将变成

  如果(IO_PORTS.bbRB3)IO_PORTS.bbLATB4 = 1;

依次将其处理为:

  如果(!!(PORTB&8))(1?(PORTB | = 16):(PORTB&=〜16));

编译器应该能够注意到?:运算符中的常量表达式,并且只需包含“ true”部分即可。通过将宏扩展为以下内容,可以减少创建的属性的数量:

  如果(IO_PORTS.ppPORTB [3])IO_PORTS.ppPORTB [4] = 1;

要么

  如果(IO_PORTS.bb(addrPORTB,3))IO_PORTS.bbPORTB(addrPORTB,4)= 1;

但是我不确定编译器是否能够很好地内联代码。

我绝不希望暗示将I / O端口当作变量使用是一个好主意,但是既然您提到C ++,这是一个有用的窍门。我自己对C或C ++的偏好是,如果不需要与使用上述样式的代码兼容,则可能是为每个I / O位定义某种类型的宏,然后为“ readBit”,“ writeBit”定义宏, “ setBit”和“ clearBit”附带条件,即传递给这些宏的位识别参数必须是打算用于此类宏的I / O端口的名称。例如,上面的示例将写为

  如果(readBit(RB3))setBit(LATB4);

并翻译为

  如果(!!(_ PORT_RB3&_BITMASK_RB3))_PORT_LATB4 | = _BITMASK_LATB4;

与C ++样式相比,预处理器的工作量要多一些,但是对于编译器,则要少一些。它还将为许多I / O实现提供最佳代码生成,并为几乎所有实现良好的代码实现。


3
从问题中引述一句话:“我不是在寻找'嘿,您可以用这种方式来做'这类答案”……
Wouter van Ooijen 2011年

我想我不太清楚您要寻找什么。当然,我希望许多对I / O引脚重构的类感兴趣的人也会感兴趣,知道使用属性可以使为一种I / O样式编写的代码几乎可以用于其他任何用途。我已经使用属性做出了类似“ LATB3 = 1;”的语句。向TCP流发送一个I / O请求。
supercat

我试图在我的问题中弄清楚:我希望能够适应新类型的IO引脚,而无需重写使用IO引脚的代码。您撰写了有关用户定义的类型转换和赋值运算符的文章,这些确实很有趣,我一直都在使用它们,但是并不能解决我的问题。
Wouter van Ooijen 2011年

@Wouter van Ooijen:您将期待什么“新型I / O引脚”?如果源代码是使用“ if(BUTTON_PRESSED)MOTOR_OUT = 1;”之类的语法编写的,那么我希望对于处理器可以读取按钮控件或电机的任何机制,几乎可以编写一个库,因此上述源代码如果按下该按钮,代码将打开电动机。这样的库可能不代表打开电动机的最有效方法,但它应该可以工作。
supercat

@Wouter van Ooijen:如果有人要求源代码在读取任何输入之前的某个时间调用UPDATE_IO()或UPDATE_INPUTS()宏,并在任何输出之后的某个时间执行UPDATE_IO()或UPDATE_OUTPUTS(),则可以提高效率。输入的语义可以在读取它们的代码或先前的UPDATE_INPUTS()/ UPDATE_IO()调用中进行采样。同样,输出可以立即发生或推迟。如果使用诸如移位寄存器之类的东西实现I / O,则延迟动作将允许合并多个操作。
supercat

1

如果您正在寻找某种非常棒的抽象硬件的方法,并且对C ++技巧有信心,则应该尝试以下模式:

https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

我曾经尝试过将它抽象为Cortex-M0芯片的硬件。我还没有写任何有关这种经验的文章(有一天我会做),但是我相信它非常有用,因为它具有静态多态性:对不同芯片使用相同的方法,无需花费任何费用(与动态多态性相比)。


自从这篇文章发表以来的几年里,我一直在为pin_in,pin_out,pin_oc和pin_in_out分别设置“类”。为了获得最佳性能(大小和速度),我使用静态类作为模板参数传递。我在柏林的会议C ++上谈到了这一点
Wouter van Ooijen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.