我在 C++ 上使用
libpgid
来根据 GPIO 事件计算 RPM。
这个想法是从给定的 GPIO 线路读取所有
RISING_EDGE
事件并递增计数器(脉冲计数器)。计数器有 3 个属性:
typedef struct counter_t
{
unsigned long count; // Incremented on every RISING_EDGE pulse
unsigned long start_timestamp; // Timestamp of the first event pulse read
unsigned long last_timestamp; // Timestamp of the last event pulse read
}
因此,在从
gpiod
读取的每个事件中,我都会:
auto event = line.event_read();
if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
{
counter->count++;
if (counter->start_timestamp == 0) counter->start_timestamp = event.timestamp;
counter->last_timestamp = event.timestamp;
}
它在监听新事件并更新计数器的线程上运行。一切正常 - 我的计数器正在正确增加。
我的问题出现在 RPM 计算上。
为此,我以 100 毫秒的固定间隔运行另一个线程,该线程读取计数器、计算 RPM 并重置计数器,如下所示:
void thread_loop()
{
std::cout << "Start thread" << std::endl;
bool first_run = true;
while (true)
{
if (!first_run)
{
// Get interval
unsigned long interval = counter->last_timestamp - counter->start_timestamp;
// Calculate speed
double speed = (counter->count / interval) * 600000000; //calculates speed
// Print results
std::cout << "SPEED: " << speed << std::endl;
std::cout << "COUNTER: " << counter->count << std::endl;
std::cout << "COUNTER START TIMESTAMP: " << counter->start_timestamp << std::endl;
std::cout << "COUNTER: LAST TIMESTAMP: " << counter->last_timestamp << std::endl;
std::cout << "INTERVAL: " << interval << std::endl;
// Reset counter
counter->reset(); // Sets count, start_timestamp and last_timestamp to 0
}
first_run = false;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
对于给定的输出,我的速度不一致:
SPEED: 0
COUNTER: 40
COUNTER START TIMESTAMP: 13953904635016
COUNTER: LAST TIMESTAMP: 13956976823743
INTERVAL: 3072188727
根据我的理解,所有
libgpiod
事件都是基于毫秒的,因此 3072188727 相当于 3072 毫秒,但我的线程以 100 毫秒的间隔运行(我预计不完全是这个值,而是大约这个值,而不是 30 次)。机器中没有运行其他任何东西,代码没有过载。
此外,速度计算始终为零。
我想我对
libgpiod
转换的理解或转换有问题,但不知道出了什么问题。我搜索了libgpiod
文档但找不到任何详细的解释。
所以,这是我的问题:
a) 我如何解释
event.timestamp
中的 libgpiod
。这是一个time_point
事件吗?纪元日期时间?是绝对的日期和时间吗?
b) 如何计算两个
libgpiod
事件之间的时间间隔(以纳秒为单位)?
c) 如何将此间隔转换为速度 (RPM) 计算?
有很多问题:
您尚未显示
counter
的声明,但必须声明volatile
,因为它是在独立执行线程中读写的。
在进行非原子访问时,还必须防止异步更改。例如,互斥锁或自旋锁。即使单个成员也可能不是原子的,例如 64 位
unsigned long
(在 Linux 上)在 32 位系统上不会是原子的。
Linux 不是实时操作系统,无法保证您的 GPIO 轮询线程能够捕获所有输入状态更改。可能是由其他线程或进程提示的时间长于脉冲周期。
最后一个问题显然不是您所显示的严重错误的原因,但有时它会导致大多数小错误,并且肯定会限制可以检测到的最大转速。您期望的最大“事件率”是多少?