Есть ли точный способ подсчета прерываний от вывода GPIO на i.MX6? - PullRequest
1 голос
/ 19 февраля 2020

У меня i.MX6 на специальной плате с разъемом для вентилятора. К сожалению, однако, линия обратной связи тахометра присоединена к выводу GPIO. Есть ли способ точно прочитать скорость вращения вентилятора? Некоторое быстрое прибегание к гуглу не выявило каких-либо счетчиков краев или чего-либо встроенного в i.MX6, которое я мог бы использовать.

Я попытался написать приложение для проверки концепции, которое подсчитывало количество прерываний, но оно ужасно неточно, и я ожидаю, что оно будет меньше, если нагрузка на процессор будет. Я вижу вентилятор, работающий на постоянной скорости, который возвращает значения от 1500 об / мин до 8k об / мин, когда фактическое значение равно 4k.

#include "os/threading/semaphore.h"
#include "os/threading/thread.h"

#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <poll.h>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

long timer_end(struct timespec start_time);
void select_gpio_number(const std::string &gpio_number);
void set_gpio_direction(const std::string &gpio_number, const std::string &direction);
void set_edge(const std::string &gpio_number);

static long period_ns = 0;

void select_gpio_number(const std::string &gpio_number)
{
    std::ofstream gpio_export("/sys/class/gpio/export");
    gpio_export << gpio_number;
    gpio_export.flush();
}

void set_gpio_direction(const std::string &gpio_number, const std::string &direction)
{
    std::ofstream gpio_direction("/sys/class/gpio/gpio" + gpio_number + "/direction");
    gpio_direction << direction;
    gpio_direction.flush();
}

void set_edge(const std::string &gpio_number)
{
    std::ofstream gpio_edge("/sys/class/gpio/gpio" + gpio_number + "/edge");
    gpio_edge << "both";
    gpio_edge.flush();
}

long timer_end(struct timespec start_time)
{
    struct timespec end_time;
    clock_gettime(CLOCK_MONOTONIC, &end_time);
    long diffInNanos = end_time.tv_nsec - start_time.tv_nsec;
    return diffInNanos;
}

int main(int, char *[], char *[])
{
    int num_samples = 0;
    std::string input_number("39"); // GPIO2_IO7 -> (2 - 1) * 32 + 7 = 39
    struct timespec start_time;
    double frequency = 0.0;

    std::ostringstream input_value;
    input_value << "/sys/class/gpio/gpio" << input_number << "/value";
    select_gpio_number(input_number);
    set_gpio_direction(input_number, std::string("in"));
    set_edge(input_number);

    int tach_value_fd = open(input_value.str().c_str(), O_RDONLY | O_NONBLOCK);

    struct pollfd tach_poll_fd;
    memset(static_cast<void *>(&tach_poll_fd), 0, sizeof tach_poll_fd);
    tach_poll_fd.fd = tach_value_fd;
    tach_poll_fd.events = POLLPRI;

    char current_tach_value;
    while (true)
    {
        // num_samples = 0;
        lseek(tach_value_fd, 0, SEEK_SET);
        poll(&tach_poll_fd, 1, -1);
        read(tach_value_fd, &current_tach_value, sizeof current_tach_value);

        if (current_tach_value == '0')
        {
            // printf("edge detected\n");
            if (num_samples >= 1)
            {
                period_ns = timer_end(start_time);

                num_samples = 0;
                frequency = 1.0 / ((period_ns + 200000.0) / 1000000000.);
                printf("Fan is spinning at: %lf Hz or %ld RPM\n", frequency, static_cast<long>(frequency * 30));

                sleep(1);
            }
            else
            {
                clock_gettime(CLOCK_MONOTONIC, &start_time);
                num_samples++;
            }
        }
    }
    return 0;
}

Есть ли что-то вопиюще неправильное в коде (да, я знаю, что функции, вероятно, должны быть stati c и тому подобное) или лучше использовать (идеально встроенный в аппаратное обеспечение)? Или я возвращаюсь к команде электриков и говорю им, чтобы они поменяли контакт?

...