如何在C中检测数字引脚是否 Flink (从高到低)?

kyks70gy  于 8个月前  发布在  Flink
关注(0)|答案(2)|浏览(71)

我正在写一个固件应用程序。我必须读取连接到智能电池的GPIO引脚,它可以传达电池的状态。引脚中的高值表示电池处于未知状态,低值表示电池已断开连接,错误由2.5 Hz Flink 指示。到目前为止,我已经成功地检测到断开连接和未知状态与以下函数:

void Check_Bat_Connected(void)
{ 
static int Low_Level_debounce = 0
if(IOPORT_LEVEL_LOW == Pin_Level)
    {
        if(25 == Low_Level_debounce)
        {
            BAT_Status = BAT_Not_Connect;
        }
        else if(Low_Level_debounce < 25)
        {
            Low_Level_debounce++;
        }
        else
        {
            /**/
        }
    }
    else
    {
        Low_Level_debounce = 0;
        BAT_Status = BAT_Unknown;
    }
}

此函数由电池处理程序在循环中调用,电池处理程序的作用是获取有关电池的信息。我添加了一个反跳变量,以增加对错误断开电池的鲁棒性。
我现在的问题是,我如何检测LED是否以2.5 Hz Flink ?我没有任何关于两次调用Check_Bat_Connected函数之间的时间间隔的信息,即使我假设频率远高于2.5 Hz。如何以稳健的方式实现最后一个检查?

5anewei6

5anewei61#

假设硬件可以在相当合理的时间间隔内提供中断,那么就有一种方法可以去抖动并检测2.5Hz信号。
使用static unsigned 32位变量通过左移和“或”运算来累积信号样本(0或1):

sample = (sample << 1) | !!currentVal; // shift and store one more bit until 32 gathered

想象一个2.5Hz的方波,采样时间约为15 ms(您需要32个样本来捕获略多于一个周期的输入信号):

00000000000011111111111111000000 // 50% clean duty cycle (not to scale)
T           T             T
01010000000010101111111111010100 // bouncing samples gathered
  • 向右 * 移动32个样本,直到检测到稳定值。这里的“稳定”是指5个连续样本均为高水平:
vvvvv // 5 lowest bits with iterative shifting
01010000000010101111111111010100 // fails 0x14 
00101000000001010111111111101010 // fails 0x0A 
00010100000000101011111111110101 // fails 0x05
00001010000000010101111111111010 // fails 0x02
00000101000000001010111111111101 // fails 0x01
00000101000000001011001011111111 // Succeeds 0x1F
            ^----1/2 period----^    XOR of 1 indicates signal is toggling

该示例数据捕获了从高到低的最近转变。如果转换是从低到高,这也同样有效。请相信以下while()循环在移位时检测到5个置位或5个复位位作为低阶位:

int range = 0;
while( range++ < 10 && (sample & 0x1F) < 0x1F || (sample & 0x1F) > 0 ) )
    sample >>= 1;

toggling = range < 10 && ((sample & 0x1) ^ ((sample >> 20) & 0x1));

一旦你有一个稳定的值在低位,异或“一个位向左在1/2的周期的信号将比较稳定的值。
如果XOR的结果为1,则信号 Flink 。
如果XOR的结果是0,则信号是稳定的(可以是高,可以是低)。
range计数器是必要的,以防止一个信号,这是从移位足够远,导致前置零的移位被考虑。
如果该检测是关键的,则可以涉及更多比特被XOR以形成关于输入信号是否交替高/低的多数决定。

vaj7vani

vaj7vani2#

如果你需要一个 * 高精度 * 的阅读时间流逝,那么边沿触发输入捕获中断是唯一的方法。但是,GPIO信号上的中断可能存在噪声,因此在这种情况下,可以考虑使用外部RC低通滤波器来消除尖峰。
否则,如果边沿触发中断是不可行的(信号太嘈杂等),最可靠的方法将是有一个循环定时器中断触发,比方说比预期频率高10倍。因此,1/2.5 = 400 ms,让中断每40 ms触发一次。
从ISR存储器内部返回一个位字段。例如,我们可以使用unsigned int来存储样本的位字段。概念伪代码:

static volatile pin_status = UNKNOWN; 

void isr (void)
{
  static unsigned int samples = 0;

  volatile bool pin_level = some_gpio_pin;
  samples <<= 1;
  samples |= pin_level;

  static const ten_samples = 0x3FFu;
  pin_status = lookup_table [samples & ten_samples]; 
}

(查找表会占用大量的flash,但还有很多其他方法可以做到这一点。您还可以将样本存储在RAM字节数组中,并使用memcmp()查找某些模式-速度较慢,占用RAM,但不占用任何闪存。
samples将结束每40 ms取得的样本的比特流,因此111.(10次)对应于高和序列0 0 0...(10倍)对应于低。你可以建立一个10个样本+/-1左右的公差,这取决于你有多挑剔。由于这个计时器运行得很慢,我们会隐式地去抖动。LED不会反弹,所以我们基本上只是在这里做一些软件EMI滤波的方式。
请记住系统时钟不准确,特别是如果您使用内部振荡器而不是外部石英。

相关问题