前卫固件:您需要的唯一边缘检测解决方案
#教程 #c #嵌入式 #arduino

我的第一个经历

我认为,当您编写第一个更复杂的Arduino程序(而不是闪烁的LED)时,这个主题是一个非常经典的话题。例如。您想计算按下一个按钮多少次。当我学习固件开发时,这是我曾经付出的首要任务。
我认为这很容易:

if(digitalRead(BUTTON)) counter++;

但是,这种方法在几秒钟内无限地增加计数器,因为它检查了按钮是否按下,而不是是否有正边缘。

在我作为电子工程师的学徒制中,我有一位老师谈到了他的政治观点,而不是编码。但是他几次谈论编码时,他确实教会了我们一些东西。他曾经教给我五行代码,这改变了我看到世界的方式以及我如何编写代码。这些是以下内容:

btn.old = btn.act;
btn.act = INPUT;
btn.edge = btn.act ^ btn.old;
btn.edge_pos = btn.edge & btn.act;
btn.edge_neg = btn.edge & btn.OLD;

我知道这看起来并不多,但是在阅读本文后,您会意识到这是完美的边缘检测方法。

如何设置

结构

为了使其尽可能多地“干净的代码”,在这种情况下使用结构非常好。

typedef struct
{
    uint8_t act;
    uint8_t old;
    uint8_t edge;
    uint8_t edge_pos;
    uint8_t edge_neg;
} edge_t;

检测

现在以下五个神圣行。它们被插入到段的顶部或无限程序循环。不要忘记初始化edge_t struct:

int main()
{
    edge_t btn = {0};

    while(1)
    {
        btn.old = btn.act;
        btn.act = digitalRead(BUTTON);
        btn.edge = btn.act ^ btn.old;
        btn.edge_pos = btn.edge & btn.act;
        btn.edge_neg = btn.edge & btn.OLD;


        // Inifinite loop, user code
    }
}

在此示例中,我正在阅读连接到Arduino上一个数字引脚的按钮的输入。

现在我们已经设置了!一开始,它看起来有些笨拙,但是您将了解下面的工作方式。

如何使用它?

如果您的电路/项目中只有一个按钮,则使用此边缘检测将非常容易。

我们有可能检测/获取按钮的以下属性:

  • 当前状态
  • 它在上一个周期中的状态
  • 状态变更
  • 正边缘
  • 负边缘

这是您可以检测/获取这些属性的方法:

当前状态

if(btn.act)
{
    Serial.println("Button is pressed!");

    //Detects if the button is pressed. equivalent of "if(digitalRead(BUTTON))"
}

上一个周期中的按钮状态

if(btn.old)
{
    Serial.println("Button was pressed last cycle!");

    //Detects if the button was pressed the cycle before
}

改变状态

if(btn.edge)
{
    Serial.println("Button State changed!");

    //Detects if the button changed from not pressed to pressed, and vice versa
}

正优势

if(btn.edge_pos)
{
    Serial.println("Button pressed down!");

    //Detects if the button changed from not pressed to pressed (Positive Edge)
}

负边

if(btn.edge_neg)
{
    Serial.println("Button released!");

    //Detects if the button changed from not pressed to pressed (Positive Edge)
}

它是如何工作的?

现在遵循这些代码行的解释:

btn.old = btn.act;
btn.act = digitalRead(BUTTON);
btn.edge = btn.act ^ btn.old;
btn.edge_pos = btn.edge & btn.act;
btn.edge_neg = btn.edge & btn.old;

为了说明布尔值的状态,我将使用级别图,其中每个正方形代表无限程序循环的一个迭代。

btn.Act

btn.act = digitalRead(BUTTON);

(在此示例中,我们再次读取连接到Arduino的数字别针的按钮输入)

ACT 值很容易理解,因为它始终具有与 DigitalRead(button)相同的值。在整个解释的其余部分中,我将当前的按钮状态称为 act

Image description

btn.old

btn.old = btn.act;
btn.act = digitalRead(BUTTON);

值是指程序循环的先前迭代中按钮的状态。它始终是上一个周期中按钮的状态。我们将价值 ACT 保存到中,然后首先我们获得了 act act 的新价值。

Image description

Btn.Edge

btn.edge = btn.act ^ btn.old;

现在变得有趣了。 “^”运算符,也称为XOR操作员,检测两个值是否不同。如果值不同,则返回true,如果值相同,则返回。因此,现在我们可以检测到按钮状态的更改,如果我们比较 act old

Image description

BTN.EDGE_POS

btn.edge_pos = btn.edge & btn.act;

所以 edge_pos 是我们都在寻找的!这检测到正边缘,或者从不按压到压的。

我们将值边缘 ACT 与AN和运算符进行比较。如果两个值都是正确的,则运算符将返回true。

如果 edge 是真的,我们知道我们的按钮状态有一个更改。如果 ACT 不平等,则检测到更改。

如果我们从不按压力到按下的更改,ACT是真实的,旧的是错误的。如果我们从按压力到不被压迫,ACT是错误的,旧的是真实的。这就是为什么我们可以将 Edge Act 与和操作员进行比较以获得正优势的原因。

Image description

BTN.EDGE_NEG

btn.edge_neg = btn.edge & btn.old;

在某些情况下,您必须读取信号的负边缘。

正如我之前所解释的那样,如果我们从按压力到不按压力有所变化, act 是错误的, 是正确的。因此,现在我们可以将 Edge 与和操作员进行比较。

Image description

多个按钮?

因此,您的项目中有多个按钮,并且每个按钮都需要Edge检测?不要以为你必须这样做:

edge_t btn1 = {0};
edge_t btn2 = {0};
edge_t btn3 = {0};
edge_t btn4 = {0};
...

由于结构中的每个值都称为UINT8_T,因此我们在每个值中都有8位可使用!我们唯一要做的不仅仅是一个按钮要做的就是重新定义 act 值。

我将举一个我有8个按钮的示例,每个按钮都连接到一个从引脚1到8的Arduino的数字引脚:

btn.act =   digitalRead(1) |
            (digitalRead(2) << 1) |
            (digitalRead(3) << 2) |
            (digitalRead(4) << 3) |
            (digitalRead(5) << 4) |
            (digitalRead(6) << 5) |
            (digitalRead(7) << 6) |
            (digitalRead(8) << 7);

(在我的下一篇博客文章中,我要解释一下。)

现在, ACT的每一点对应于一个按钮。

Image description

如果(btn.edge_pos),我们不能使用。我们需要指定要检测到的按钮。这可以用一点口罩完成。

我喜欢创建为每个按钮定义

//                  HEX         BINARY
#define BUTTON_1    0x01    //  00000001
#define BUTTON_2    0x02    //  00000010
#define BUTTON_3    0x04    //  00000100
#define BUTTON_4    0x08    //  00001000
#define BUTTON_5    0x10    //  00010000
#define BUTTON_6    0x20    //  00100000
#define BUTTON_7    0x40    //  01000000
#define BUTTON_8    0x80    //  10000000

我给出的每个值定义为 uint8_t Act

从这里开始,它很简单。例如,如果要检测到第三个按钮的正边缘,我们可以做到这一点:

if(btn.edge_pos & BUTTON_3)

使用此位掩码,我们允许它仅检测到特定位的更改。

结局

感谢您的阅读!我鼓励您继续进一步探索此主题,看看如何将其应用于项目。我很高兴地宣布,我将继续在固件基础上继续本系列!

随时在下面留下任何评论或问题,我会尽力帮助您!

愿您的代码无漏洞!