我的第一个经历
我认为,当您编写第一个更复杂的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 。
btn.old
btn.old = btn.act;
btn.act = digitalRead(BUTTON);
旧值是指程序循环的先前迭代中按钮的状态。它始终是上一个周期中按钮的状态。我们将价值 ACT 保存到旧中,然后首先我们获得了 act act 的新价值。
Btn.Edge
btn.edge = btn.act ^ btn.old;
现在变得有趣了。 “^”运算符,也称为XOR操作员,检测两个值是否不同。如果值不同,则返回true,如果值相同,则返回。因此,现在我们可以检测到按钮状态的更改,如果我们比较 act 和 old 。
。BTN.EDGE_POS
btn.edge_pos = btn.edge & btn.act;
所以 edge_pos 是我们都在寻找的!这检测到正边缘,或者从不按压到压的。
。我们将值边缘和 ACT 与AN和运算符进行比较。如果两个值都是正确的,则运算符将返回true。
如果 edge 是真的,我们知道我们的按钮状态有一个更改。如果 ACT 和旧不平等,则检测到更改。
如果我们从不按压力到按下的更改,ACT是真实的,旧的是错误的。如果我们从按压力到不被压迫,ACT是错误的,旧的是真实的。这就是为什么我们可以将 Edge 和 Act 与和操作员进行比较以获得正优势的原因。
BTN.EDGE_NEG
btn.edge_neg = btn.edge & btn.old;
在某些情况下,您必须读取信号的负边缘。
正如我之前所解释的那样,如果我们从按压力到不按压力有所变化, act 是错误的, 是正确的。因此,现在我们可以将 Edge 和旧与和操作员进行比较。
多个按钮?
因此,您的项目中有多个按钮,并且每个按钮都需要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的每一点对应于一个按钮。
如果(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)
使用此位掩码,我们允许它仅检测到特定位的更改。
结局
感谢您的阅读!我鼓励您继续进一步探索此主题,看看如何将其应用于项目。我很高兴地宣布,我将继续在固件基础上继续本系列!
随时在下面留下任何评论或问题,我会尽力帮助您!
愿您的代码无漏洞!