我正在编写一个固件应用程序。我必须读取连接到智能电池的 GPIO 引脚,该引脚传达电池的状态。引脚中的高值表示电池处于未知状态,低值表示电池已断开连接,并且通过 2.5 Hz 闪烁指示错误。到目前为止,我已经成功地使用以下函数检测断开连接和未知状态:
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_1 = BAT_Unknown;
}
}
此函数由电池处理程序循环调用,其作用是获取有关电池的信息。我添加了一个去抖变量,以提高针对电池错误断开的鲁棒性。
我现在的问题是,如何检测 LED 是否以 2.5 Hz 的频率闪烁?即使我假设频率远高于 2.5 Hz,我也没有关于两次调用 Check_Bat_Connected 函数之间经过多长时间的信息。我怎样才能以稳健的方式实施最后的检查?
如果您需要“高精度”读取经过的时间,那么边沿触发的输入捕获中断是唯一的方法。但是,如果 GPIO 信号出现潜在噪声中断,就会出现问题,因此在这种情况下,可以考虑使用外部 RC 低通滤波器来消除尖峰。 否则,如果边沿触发中断不可行(信号噪声太大等),最可靠的方法是使用循环定时器中断触发,假设频率比预期频率高 10 倍。所以 1/2.5 = 400ms,让中断每 40ms 触发一次。
从 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];
}
(查找表会占用大量闪存,但还有许多其他方法可以做到这一点。您还可以将样本存储在 RAM 字节数组中,并使用 memcmp() 来查找某些模式 - 速度较慢且消耗 RAM,但不占用任何闪光灯。)
samples
最终会得到每 40ms 采样一次的比特流,因此 1 1 1...(10 次)的二进制序列对应于高位,0 0 0...(10 次)的序列对应于高位到一个低点。您可以建立 10 个样本 +/-1 左右的容差,具体取决于您的挑剔程度。由于这个计时器运行得很慢,我们会隐式地进行去抖...LED 不会反弹,因此我们本质上只是在这里进行某种软件 EMI 滤波。
请记住系统时钟的不准确性,特别是当您使用内部振荡器而不是外部石英运行时。