【问题标题】:How to create an advanced counter in C?如何在 C 中创建高级计数器?
【发布时间】:2011-12-12 15:15:30
【问题描述】:

我有一个无限 for 循环,其中包含一个从 LDR 连续接收读数的函数。代码是这样的:

.
light_1=sample(1);
.
.
for(::){
 light_2=sample(2);
 if((light_2 > (light_1))
 alarm=1;
 delay(1); // wait for 1 second
.
.
.
}etc

light_2 是现在采样的灯光,与第一个采样的灯光 light_1 进行比较。

我想要做的是创建一个计数器,如果 light_2 大于 light_1 3 次,则中断循环。但不仅如此,灯会以 1 秒的间隔闪烁 3 次。我希望只有当灯在 4 秒内闪烁 3 次时才能中断循环。

如果这没有意义,请告诉我,以便我更好地解释。



编辑 3

我希望它继续执行正常的采样时间,并且仅在获得 3 个读数实例时才中断循环。但是我仍然觉得我应该使用计时器??请帮忙。

额外信息:我在这里回复了一位回答者,也许其他人可以从这个额外的解释中受益:

有两个阶段:

  1. 第 1 阶段:一次检测一个实例。 (获得了适用于此的代码)
  2. 第 2 阶段:检测光线(连续闪烁 3 次)以执行其他功能。

我应该避免使用太多的延迟来让第 2 阶段工作,因为这会影响第 1 阶段的阅读准确性和速度。

如果您需要更多解释,请告诉我!

【问题讨论】:

  • 提示:确保比较考虑了阅读器的噪音。环境光条件的任何变化都可能导致错误检测。
  • 是的,我也在尝试解决这个问题 :)
  • 您想检测 3 次不同的闪光,在 2 次闪光之间恰好间隔 1 秒?或 3 次闪烁,但周期可以不同于 1 秒?如果是这样,请检查我的答案...我写了 2 个代码,每个案例一个...
  • 抱歉,我不得不投反对票。经过 3 次编辑和几个答案后,这仍然非常混乱和不清楚。

标签: c timer counter


【解决方案1】:

使用某种结构将各种参数组合在一起。这通常称为“状态”。

struct state {
     int light_2_greater_than_1;
     int other_things;
};

然后更新该对象,并检查其成员。

【讨论】:

  • 您能否详细说明您的意思?
  • 您要求一种更“复杂”的计数器机制,而不是使用一个整数作为“计数器”。因此,请使用保存状态的结构,而不是整数。
【解决方案2】:

这是一个非常简单的示例,说明了使用 SIGALRM 来保持时间独立于您的 sample() 函数,这是在 sample() 需要一些时间才能完成的情况下,您总是可以在之前执行 now = time(NULL)睡眠/延迟

#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>

time_t now;
void alarm_handler(int sig);
struct flash {
    time_t first_stamp;
    int count;
};
int main(int ac, char *av[]) {
    signal(SIGALRM, alarm_handler);
    now = time(NULL);
    struct flash f = {0,0};
    int light = 0;
    alarm(1); 
    for (;;) {
//      light = sample.. 
        if (light) {
            if (f.count) {
                if (f.count > 3 && (now - f.first_stamp) > 4) {
//                  do_whatever_function or break;
                    f.count = 0;
                    f.first_stamp = 0;
                } else {
                    f.count++;
                }
            } else {
                f.first_stamp = now;
                f.count = 1;
            }
        }
        sleep(1);
    }
    return 0;
}
void alarm_handler(int sig) {
    now = time(NULL);
    alarm(1);
}

编辑: 实际上现在我读到它 time() 不是信号安全的,请给我一点时间来修改代码

第二次编辑: http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04 实际上它是信号安全的:)

下表定义了一组函数,这些函数要么是可重入的,要么是不可被信号中断的,并且是异步信号安全的。因此,应用程序可以不受限制地从信号捕获函数调用它们: ...时间()

【讨论】:

  • 感谢您提供信息丰富的回复,我想忽略 4 秒间隔条件,因为它增加了代码的复杂性。但这看起来可以工作,会试一试!
【解决方案3】:

计数器可以很简单:

int count = 0;

...

if (condition)
{
    count++;
}

现在,要将其合并到您的循环中,您可以执行以下操作:

int count = 0;

while (count < THRESHOLD)
{
    ...
}

【讨论】:

  • 是的,但那是一个简单的计数器。我怎么能说如果这种情况发生在不到 4 秒的时间内,然后中断?
  • @Fendi,始终保留最后三个闪烁的时间戳(即,对于每个新的闪烁,保留新的一个并将第四个抛出)并使您的 while 条件如下: while (count &lt; THRESHOLD || now - time_of_third_last_flash &gt; 4seconds)
【解决方案4】:

您想检测 2 次闪光之间恰好 1 秒发出的 3 次不同闪光吗? 所以 2 秒内闪烁 3 次(不是 4 次)...? (flash1 是开始,flash2 1 秒后,flash2 2 秒后。所以 2 秒内闪 3 次)。

这是一个简单的代码,没有计时器,也没有执行此检测的数组:

light_1=sample(1);
for(::){
    // the while loop waits for the first flash, ie : wait for sample(2) > light_1
    while(sample(2) <= (light_1))
    { 
        // if you know that a flash lasts n ms, you can add "sleep(n);" in this loop,  so you will use less cpu.
    }
    // now the first flash is emitted, we will wait for 1 second before looking for the second flash
    delay(1);
    if(sample(3) <= light_1)
        continue;
    // if after 1 second, there is no flash, ther will not be 3 flashes in 2 seconds.
    // so we restart from the first flash (the "continue;" instruction do that)   
    delay(1); // the second flash was catched, then we wait for 1 second before looking for the third flash.
    if(sample(4) <= light_1)
        continue;
    //so if sample(4 > light_1), we have 3 flashes, each 2 are separated by 1 second then you can do the function you want.
    do function...
    // once finish the function, we loop back to the beginning to start looking for the first flash again.
}

您必须确保在发出闪光灯时未拍摄 light_1 = sample(1)。 我想到了两种方法:

1 - 如果你知道闪光灯的长度(例如:20 毫秒),取 3 或 4 个与闪光灯的长度分开的值,以获得好的 light_1 值:

int i; 
light_1 = sample(1); 
for(i=0 ; i<4 ; i++)
{
    light_1 = sample(1) < light_1 ? sample(1) : light_1; 
    sleep(20); // 20 = 20 ms. you can replace 20 by lenght_of_flash
}

2 - 通过校准。然后你可以使用没有闪光灯获得的值来初始化light_1;


如果你想在不知道周期的情况下在 4 秒内获得 3 次闪烁,我认为有必要使用计时器:

这里是代码:

light_1 = sample();
int nb_flashes = 0;
bool fail = false;
clock_t start;  to contain the time
for(::)
{
    fail = false;
    nb_flashes = 0;
    start = clock();  //get the time to count 4 seconds.
    while(nb_flashes <3)
    {
       while(sample() <= (light_1))
      { 
        sleep(1);
      }
      nb_flashes ++; 
      if( (clock() - start)/CLOCKS_PER_SEC >=4)
      {
        fail = true;  //more than 4 seconds where elapsed... so we restart from the beginning of the for loop.
        break;  //going outside the while loop
      }

      while(sample() > light_1 )
        sleep(1);  //waiting the end of the irst flash.

      }
      if (fail)
        continue; // there was no 3 flashes whithin 4 seconds, so we restart from the beginning of the for loop 
      else
        do function // do the function you want...


}

【讨论】:

    【解决方案5】:

    也许我弄错了,但是下面的代码还不够吗?

    // Data type and initial value for these two vars must be apt for this scenario.
    int last_light = 0;
    int current_light = 0;
    
    for(uint8_t crescent_flashes = 0; TRUE; last_light = current_light){
      current_light = sample();
    
      if(current_light <= last_light){
        crescent_flashes = 0;
        continue;
      }
    
      crescent_flashes++;
    
      if(crescent_flashes == 3){
        do_smart_stuff();
        crescent_flashes = 0;
      }else{
        sleep(1second);
      }
    }
    

    【讨论】:

    • 不明确的部分是什么?
    • for循环,能解释一下条件吗?
    • 无法格式化,抱歉。 uint8_t crescent_flashes = 0; // 简单的值初始化 TRUE; // 永远运行循环 last_light = current_light // 在每个循环结束时, current_light 变为 last_light
    【解决方案6】:

    问题比您预期的要大 10 倍。如果你想自己解决问题,首先,别管键盘,试着设计这些东西。

    基本上,您只有一个关于灯的信息:开或关。这对于如此复杂的操作是不够的,比如在指定的时间间隔内检查闪存计数。您必须创建衍生物

    首先,您必须设置某种计数器或时间戳,用于计算时间间隔,例如经过的时间、两个事件之间的距离等。它可以是时间戳(由操作系统或您使用的语言提供),或者如果您的程序是一个无限循环,您可以将 sleep() 和 counter++ 放入其中,并使用它来测量时间(OK,它不太准确,因为它不计入程序运行期间的时间,但在这个时间精度上并不重要)。

    您的衍生变量将类似于:

    • 灯光变化;参数:方式(上/下)、时间戳
    • flash:一系列事件,其中第一个向上,第二个向下,参数:开始、结束和(根据这些计算:)持续时间。

    您必须检测并存储这些事件,最少是最后 N 个事件,其中 N 是您的条件中出现的最大数字(例如,如果您想检测 3 次闪光,则必须至少存储最后 3 次闪光)。也许,环形缓冲区是最好的解决方案。

    当您检测到一系列事件时,您必须检查每次更改的情况,例如检查灯是否在最后 X 秒内闪烁。

    使用纸,绘制时间线,命名并绘制您的术语(如事件),“在纸上”运行您的检测算法。

    再说一遍:你正在解决的问题甚至比你第一次遇到的困难更大。但是如果将问题拆分为更小的问题层(第 1 层:原子更改 -> 事件;第 2 层:事件 -> 事件队列;第 3 层:事件序列分析),它们都是小问题,很容易解决。

    首先,在多层结构中很难思考,很难不混合层,但如果你有适当的低层,你可以在上层做更复杂的魔法,或者构建更复杂的层,例如检测莫尔斯信号。

    【讨论】:

    • 希望您能够编写自己的代码。这是一个非常好的做法。也许,你会返回 amd 返回一些问题:为什么我需要存储最后 N 个事件而不是最后 1 个?我应该如何避免非真实的事情,例如,当两个“灯亮”出现而它们之间没有“灯熄灭”事件时?不管怎样,最终的程序看起来很简单,你也会发现它也很简单。 (注意:我和朋友写了一个完整的家庭自动系统,我遇到了类似的问题。)
    【解决方案7】:

    我假设您想要做的是:检测到灯在过去 4 秒内闪烁了 3 次。通过“闪光”,我认为您的意思是灯熄灭然后又亮了。 (当然,“闪光灯”可能是指灯必须先亮再灭,这需要更复杂的解决方案,但是由于您没有给我们定义闪光灯的含义,因此我将选择更简单的版本。)

    首先,检查您现在的灯光读数是否大于或小于您在过去某个时间点的灯光读数不会告诉您灯光是打开还是关闭。您需要建立一个低阈值,低于该阈值您将认为灯关闭,以及一个高阈值您将认为灯打开。当读数低于低阈值时,您将忽略它。当读数在两个阈值之间变化时,您也会忽略它。当读数超过高阈值时,你猜到了,你再次忽略它。你只关心以下两种情况:

    1. 灯熄灭,之前的读数低于高阈值,新的读数高于高阈值;这意味着灯现在亮着。

    2. 灯亮,之前的读数高于低阈值,新的读数低于低阈值;这意味着灯现在已关闭。

    如果你不这样做,那么你会得到很多错误的触发,因为你的 LDR 读数会随着有人穿着高反光的白色实验室外套走路而变化,或者仅仅是因为随机噪音。

    而且由于我们使用了 flash 来表示从“关闭”到“开启”的转换,因此您实际上只需要关心上面的第一种情况。 (否则,您需要同时考虑这两种情况。)

    因此,您需要做的是有一个包含三个条目的数组,您可以在其中存储最后三个闪烁中的每一个发生的时间。每次发生新的闪存时,您都会丢弃数组第一个元素中的时间值,将接下来的两个向下移动,并将新的时间值存储在数组的最后一个元素中。每次执行此操作时,您都会检查第一个条目和最后一个条目之间的差异是否小于或等于 4 秒。

    注意:在任何情况下都不应使用睡眠、等待或延迟功能,因为当您的程序处于睡眠状态时,灯可能会闪烁,您会错过它。

    【讨论】:

    • 感谢您提供信息丰富的回复,我唯一的问题是我无法从 ADC 获得正确的读数,我得到的只是噪音。这使我无法提供响应某个阈值的条件,因此我必须依赖于现在采样光大于之前采样光的条件。
    • 如果你得到的all都是噪音,那么在这个项目上工作没有意义,因为它永远不会工作。如果您收到一些噪音,甚至很多噪音,那么这就是正是您应该使用我建议的阈值而不是您的阈值的原因过于简单的方法。
    • 然后我必须找到一种方法来过滤结果。也许我读错了别针。
    • 是的,您肯定需要首先确定您从硬件获得了良好的输入,然后才能希望在软件中得到任何工作。
    【解决方案8】:

    我认为您的描述可能意味着您永远不会中断(如果您的条件没有在 3 秒内发生)。那是你的意图吗?我有一种感觉,一旦您采样 4 次,您就想打破,并且样品 2-4 高于样品 1;这发生在 3 秒内。

    对于您的问题的解释,您可以将计数器创建为具有以下字段的结构:

    s1、s2、s3、s4 数数 t1, t2, t3, t4

    将全部初始化为0。在第一个样本到达时,将值存储在s1中,并将其jiffies存储在t1中。

    迭代:如果新样本 s1 且 t_elapsed

    如果任何新样本 3 秒,则向下旋转:s2 和 t2 到 s1 和 t1,s3/t3 到 s2/t2,以及 s4/t4 到 s3/t3。如果 s1 >= s2,再次向下旋转。一次(如果)s1

    这显然不是完全精确的,因为算法中经过了时间。您还面临进程抢占、中断等的风险,并且可能想考虑这是否可以接受,或者您是否想使用互斥锁等保护您的结构。

    【讨论】:

    • 它应该是:一旦(如果)s1
    • 非常感谢您的回复。我想尽量避免使用时间条件,因为我意识到它的代码非常复杂(我在编程方面效率不高)。所以我想有一个数组来存储值并在数组已满时打破循环。我将从我自己的函数中添加 1 秒的延迟,以将数组中的每个值存储为每秒 1(样本)。如果数组已满,我可以中断循环吗?
    猜你喜欢
    • 1970-01-01
    • 2013-03-03
    • 2021-12-18
    • 2017-10-30
    • 2021-05-12
    • 2021-05-09
    • 2014-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多