Nano Hash - криптовалюты, майнинг, программирование

Доступ к плате GPIO AM335x в качестве СБРОСА и минимального использования ЦП

В моем приложении есть поток, который заботится о кнопке RESET, когда и сколько времени она была нажата; на основании этого мы принимаем меры. Проблема состоит в том, чтобы минимизировать использование ЦП:

1.popen использование fp=popen(RESET_GPIO_VALUE,"r"); с этим приложением имеет 75% CPU consumption.

  1. fopen использования fp=fopen(RESET_GPIO_VALUE,"r"); с этим приложением есть 87% CPU consumption.

  2. open использования fd = open(RESET_GPIO_VALUE,O_RDONLY); с этим приложением есть 95% CPU consumption.

Есть ли способ получить доступ к GPIO примерно с 10-15% of CPU consumption.

В настоящее время в моей логике я постоянно проверяю RESET GPIO, проверяя его значение с помощью определенных выше методов. Когда кнопка нажата, таймер запускается и отпускается, останавливает таймер и вычисляет разницу во времени. Все это действие работает в бесконечном цикле.


  • Как вы получили данные о потреблении? Какой код после открытия файла? 14.03.2016
  • Как можно рассчитать общее потребление при открытии конкретного файла? 14.03.2016
  • @Suneet В основном, если я правильно понимаю вашу проблему, вы хотите получить прерывание на GPIO, если в любой момент будет нажат какой-либо переключатель. Чтобы вы могли предпринять какое-то желаемое действие. Но вы не хотите использовать метод опроса? Если это проблема, мы можем это реализовать. Вы можете пояснить это мне. ТАК, что мы можем помочь. 14.03.2016
  • Да, мистер Саху: В настоящее время в моей логике я постоянно проверяю RESET GPIO, проверяя его значение с помощью определенных выше методов. Когда кнопка нажата, таймер запускается и отпускается, останавливает таймер и вычисляет разницу во времени. Все это действие работает в бесконечном цикле. 14.03.2016
  • @SudiptaKumarSahoo: в настоящее время в моей логике я постоянно проверяю RESET GPIO, проверяя его значение с помощью определенных выше методов. Когда кнопка нажата, таймер запускается и отпускается, останавливает таймер и вычисляет разницу во времени. Все это действие работает в бесконечном цикле. 14.03.2016
  • Используете ли вы какой-либо метод опроса? 14.03.2016
  • Окей добавит в него решение с помощью Linux SIGNAL, чтобы не нужно было его опрашивать. 14.03.2016
  • @SudiptaKumarSahoo: Не могли бы вы рассказать подробнее о том, как я могу получить сигнал и прошедшее время с помощью захвата сигнала, не заходя в бесконечный цикл или метод объединения. 14.03.2016
  • ваш вопрос, вероятно, был бы намного яснее, если бы вы показали нам, что такое RESET_GPIO_VALUE на самом деле... 14.03.2016
  • @mfro: RESET_GPIO_VALUE /sys/класс/gpio/gpio20/значение 14.03.2016
  • Для popen становится RESET_GPIO_VALUE "cat /sys/class/gpio/gpio20/value" 14.03.2016
  • @SuneetTimes Я дал полное решение. Пожалуйста, просмотрите его и попытайтесь понять, оно решит вашу цель. Любой запрос всегда приветствуется. Пожалуйста, примите и подтвердите его, чтобы он мог помочь и другим. 14.03.2016

Ответы:


1

С (изначально) отсутствующей информацией о RESET_GPIO_VALUE мы можем дать разумный ответ.

Очевидно, что пакет поддержки вашей платы микроконтроллера реализует Linux интерфейс GPIO Sysfs для пользовательского пространства (вы должны прочитать документацию об этом, которая поставляется с вашим Linux BSP).

По сути (если плата и драйвер поддерживают это), вы можете создавать триггерные прерывания GPIO в пользовательском пространстве. Просто напишите rising, falling или both (буквально) в /sys/class/gpio/gpioN/edge, чтобы выбрать края сигнала, на которые ваш код хочет реагировать, затем выполните poll() в дескрипторе открытого файла для /sys/class/gpio/gpioN/value.

Этот опрос будет возвращаться каждый раз, когда value изменяется соответствующим образом.

14.03.2016

2

Хорошо, хотя это немного сложно, я расскажу вам, какие шаги необходимы для этого. Поскольку это действительно сложно, многие не хотели бы давать правильный ответ, тем не менее, это шаг, которому вы можете следовать.

1. Что вам нужно сделать, так это сначала написать модуль ядра. Затем вы можете скомпилировать это для создания файла .ko. Это загружаемый модуль ядра. Напишите загружаемый модуль ядра, в котором вы можете сохранить ISR (процедура обслуживания прерываний), которая запускается, когда на какой-либо конкретный GPIO приходит прерывание. К вашему сведению, ISR также является функцией, которая автоматически запускается при получении прерывания на конкретный GPIO.

Оставьте в своем модуле ядра возможность принимать прикладной уровень Process ID, чтобы, как только произойдет прерывание на GPIO, модуль мог отправить SIGNAL с уровня ядра на прикладной уровень.

2. Скопируйте это в свой ARM target processor, а затем вставьте модуль в ядро. Вы можете вставить модуль, набрав

             `insmod <your_module.ko>`
  1. Проверьте, вставлен ли модуль, введя команду lsmod на терминал вашей целевой платы AM335x.

3. Затем вы можете написать приложение уровня пользователя, где вы можете отправить processID процесса с уровня приложения на уровень ядра, как только ваш процесс запустится. ТАК, чтобы модуль ядра, который вы написали, мог заставить прикладной уровень ProcessID отправить файл SIGNAL.

ПРИМЕЧАНИЕ. Поскольку Linux по умолчанию использует первые 32 SIGNAL, вы можете использовать для своих целей номер SIGNAL из 33 to 64. Здесь мы используем SIGNAL Number 44.

Пример модуля ядра:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>                 //! << Required for the GPIO functions
#include <linux/interrupt.h>            //! << Required for the IRQ code

#include <asm/siginfo.h>            //! << siginfo
#include <linux/rcupdate.h>         //! << rcu_read_lock
#include <linux/sched.h>            //! << find_task_by_pid_type
#include <linux/debugfs.h>
#include <linux/uaccess.h>


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sudipta Kumar Sahoo");
MODULE_DESCRIPTION("A SPI GPIO Interrupt driver for the Sitara");
MODULE_VERSION("1.0");

#define SIG_TEST 44             //! << we choose 44 as our signal number
                    //! << (real-time signals are in the range of 33 to 64)
struct dentry *file;

int gPID = 0;
int Return = 0;
struct siginfo stGInfo;
struct task_struct *stGTask;

//static unsigned int resetGPIOInterrupt = 115;   //! << hard coding the Delphino Interrupt gpio for this in BB P9_27 (GPIO115)
static unsigned int resetGPIOInterrupt = 54;
static unsigned int irqNumber;                 //! << Used to share the IRQ number within this file
static unsigned int numberPresses = 0;         //! << For information, store the number of times the SIGNAL is high.

/********************************************************************************/
/**
 * \fn    write_pid(struct file *file, const char __user *buf,
 *                                                  size_t count, loff_t *ppos)
 *
 * @brief The LKM write_pid function
 *        The static keyword restricts the visibility of the function
 *        to within this C file.
 *  
 * @return returns 0 if successful
 */
/********************************************************************************/
static ssize_t write_pid(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        char mybuf[10];
        int pid = 0;
        int ret;
        struct siginfo info;
        struct task_struct *stTask;
        /* read the value from user space */
        if(count > 10)
    {
        return -EINVAL;
    }

        copy_from_user(mybuf, buf, count);      //! << Copy the Process ID from the Application Process

    /* For Global variables value assignment starts */
    /******************************************************************************************/
    //! Copy the required information to Global variables to send SIGNAL from ISR
    sscanf(mybuf, "%d", &gPID);         //! << Copy to global PID
    memset(&stGInfo, 0, sizeof(struct siginfo));
    stGInfo.si_signo = SIG_TEST;
        //! << this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
        //! << and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data
        //! << is not delivered to the user space signal handler function.
    stGInfo.si_code = SI_QUEUE;
    stGInfo.si_int = 1024;
    /*For Global variables value assignment Ends */
    /******************************************************************************************/

        /* send a SIGNAL to the process to intimate that it received the corresponding PID. */
        sscanf(mybuf, "%d", &pid);
        printk("pid = %d\n", pid);
        memset(&info, 0, sizeof(struct siginfo));
        info.si_signo = SIG_TEST;
        //! << this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
        //! << and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data
        //! << is not delivered to the user space signal handler function.
        info.si_code = SI_QUEUE;
        info.si_int = 1234;                     //! << real time signals may have 32 bits of data.

        rcu_read_lock();                //! << Get the RCU Read lock
    //t = find_task_by_pid_type(PIDTYPE_PID, pid);  //! << find the task_struct associated with this pid
        stTask = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);
        if(stTask == NULL)
        {
                printk("no such pid\n");
                rcu_read_unlock();
                return -ENODEV;
        }
        rcu_read_unlock();              //! << Make the RCU Read unlock

        ret = send_sig_info(SIG_TEST, &info, stTask);    //! <<send the signal
        if (ret < 0)
        {
                printk("error sending signal\n");
                return ret;
        }
        return count;
}

/********************************************************************************/
/**
 * @brief The LKM mapping user defiend functions to file_operations structure.
 */
/********************************************************************************/
static const struct file_operations my_fops = {
        .write = write_pid,
};

/********************************************************************************/
/**
 * @brief Function prototype for the custom IRQ handler function
 *        see below for the implementation.
 */
/********************************************************************************/
static irq_handler_t  ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs);

/********************************************************************************/
/**
 * \fn    __init ebbgpio_init(void)
 *
 *  @brief The LKM initialization function. The static keyword restricts the
 *         visibility of the function to within this C file. The __init macro
 *         means that for a built-in driver (not a LKM) the function is only used
 *         at initialization time and that it can be discarded and its memory
 *         freed up after that point. In this example this function sets up the
 *         GPIOs and the IRQ
 *
 *  @return returns 0 if successful
 */
/********************************************************************************/
static int __init ebbgpio_init(void)
{
   int result = 0;
   printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM\n");
   // Is the GPIO a valid GPIO number (e.g., Sitara has 4x32 but not all available)
   if (!gpio_is_valid(resetGPIOInterrupt))
   {
      printk(KERN_INFO "GPIO_TEST: invalid Button GPIO\n");
      return -ENODEV;
   }
   //Going to set up the Button. It is a GPIO in input mode and will be zero  by default
   //gpio_set_value(gpioLED, ledOn);                   //! Not required as set by line above (here for reference)
   gpio_request(resetGPIOInterrupt, "sysfs");       //! Set up the gpioButton
   gpio_direction_input(resetGPIOInterrupt);        //! Set the resetGPIOInterrupt GPIO to be an input
   gpio_set_debounce(resetGPIOInterrupt, 200);      //! Debounce the resetGPIOInterrupt with a delay of 200ms
   gpio_export(resetGPIOInterrupt, false);          //! Causes gpio115 to appear in /sys/class/gpio
                                           //! the bool argument prevents the direction from being changed

   // Perform a quick test to see that the resetGPIOInterrupt is working as expected on LKM load
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt state is currently: %d\n", gpio_get_value(resetGPIOInterrupt));

   // GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us
   irqNumber = gpio_to_irq(resetGPIOInterrupt);
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt is mapped to IRQ: %d\n", irqNumber);

   // This next call requests an interrupt line
   result = request_irq(irqNumber,                       //! The interrupt number requested
                        (irq_handler_t) ebbgpio_irq_handler, //! The pointer to the handler function below
                        IRQF_TRIGGER_RISING,             //! Interrupt on rising edge (When The Signal is High-1 not at Low-0)
                        "ebb_gpio_handler",              //! Used in /proc/interrupts to identify the owner
                        NULL);                           //! The *dev_id for shared interrupt lines, NULL is okay

   printk(KERN_INFO "GPIO_TEST: The interrupt request result is: %d\n", result);

   /* we need to know the pid of the user space process
   * -> we use debugfs for this. As soon as a pid is written to 
   * this file, a signal is sent to that pid
   */
   /* only root can write to this file (no read) */
   file = debugfs_create_file("signalconfpid", 0200, NULL, NULL, &my_fops);

   return result;
}


/********************************************************************************/
/**
 *  \fn    __init ebbgpio_init(void)
 *
 *  @brief The LKM cleanup function Similar to the initialization function,
 *         it is static. The __exit macro notifies that if this code is used for
 *         a built-in driver (not a LKM) that this function is not required. Used
 *         to release the GPIOs and display cleanup messages.
 *
 *  @return returns NULL
 */
/********************************************************************************/
static void __exit ebbgpio_exit(void)
{
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt state is currently: %d\n", gpio_get_value(resetGPIOInterrupt));
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt was received %d times\n", numberPresses);

   free_irq(irqNumber, NULL);                          //! << Free the IRQ number, no *dev_id required in this case.
   gpio_unexport(resetGPIOInterrupt);               //! << Unexport the resetGPIOInterrupt GPIO.
   gpio_free(resetGPIOInterrupt);                   //! << Free the resetGPIOInterrupt GPIO.

   debugfs_remove(file);                   //! <<  Remove the debugfs file.
   printk(KERN_INFO "GPIO_TEST: Unloading the GPIO_TEST LKM module!\n");
}

/********************************************************************************/
/**
 *  \fn    ebbgpio_irq_handler(void)
 *  @brief The GPIO IRQ Handler function. This function is a custom interrupt
 *         handler that is attached to the GPIO above. The same interrupt handler
 *         cannot be invoked concurrently as the interrupt line is masked out
 *         until the function is complete. This function is static as it should
 *         not be invoked directly from outside of this file.
 *
 *  @param irq    the IRQ number that is associated with the GPIO--useful for logging.
 *
 *  @param dev_id the *dev_id that is provided -- can be used to identify which
 *                                                device caused the interrupt
 *                                      (Not used in this case as NULL is passed.)
 *
 *  @param regs   h/w specific register values -- only really ever used for debugging.
 *
 *  @return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise.
 */
/********************************************************************************/
static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs)
{
   printk(KERN_INFO "GPIO_TEST: Interrupt! (resetGPIOInterrupt state is %d)\n", gpio_get_value(resetGPIOInterrupt));
   numberPresses++;                         // Global counter, will be outputted when the module is unloaded

   /* Sending The Signal to Process Starts */
   rcu_read_lock();
   stGTask = pid_task(find_pid_ns(gPID, &init_pid_ns), PIDTYPE_PID);
   printk("sending signal From ISR\n");
   if(stGTask == NULL)
   {
      printk("no such pid\n");
      rcu_read_unlock();
      return -ENODEV;
   }
   rcu_read_unlock();

   //This is the Line Important for You. Where we are sending the Signal to the Application layer
   Return = send_sig_info(SIG_TEST, &stGInfo, stGTask);    //send the signal

   if (Return < 0)
   {
      printk("error sending signal\n");
      return Return;
   }
   /* Sending The Signal to Process Ends */

   return (irq_handler_t) IRQ_HANDLED;      // Announce that the IRQ has been handled correctly
}

/********************************************************************************/
/// This next calls are  mandatory -- they identify the initialization function
/// and the cleanup function (as above).
/********************************************************************************/
module_init(ebbgpio_init);
module_exit(ebbgpio_exit);

Вы можете прямо взять код и создать для него файл .ko. Просто позаботьтесь о настройке номера appropriate GPIO в соответствии с вашими требованиями. Я настроил GPIO-54 Код был написан только для AM335x процессора. Если вы не знаете, как создать файл .ko, просто будьте спокойны и погуглите "HOW TO BUILD A KERNEL MODULE", вы легко его получите.

Наконец, вы можете написать фиктивное приложение для его тестирования. Приложение выглядит следующим образом:

/********************************************************************************/
/**
 * @file   userApplication.c
 *
 * @author Sudipta Kumar Sahoo
 *
 * @brief  An Application layer programm implementation to connect with the
 *     kernel layer, That receives the interrupt from the kernel space
 *     once the particular interrupt is received at configured GPIO pin.
 *
 *     NOTE:: Before executing the program first mount the debugfs if it has
 *     not already mounted to the file system and then execute the application
 *     to receive the interrupt Signal in applicatio layer.
 *
 *  The copyright notice does not evidence any actual or intended publication.
*/
/********************************************************************************/

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#define SIG_TEST    44 /* we define our own signal, hard coded since SIGRTMIN
                is different in user and in kernel space */ 
#define RUNNING     1
/********************************************************************************/
/**
 * \fn    void signalHandler(int n, siginfo_t *info, void *unused)
 *
 * @brief The receiveData function
 *        The function has been registered to the kernel layer debugfs file system
 *        When GPIO gets the interrupt it intimates to appliation process via
 *    signalHandler() function and the appropriate action is taken afterwards.
 *  
 * @return NULL
 */
/********************************************************************************/
void signalHandler(int n, siginfo_t *info, void *unused)
{
    printf("Application received value %i\n", info->si_int);
    /* Do what ever you want to do inside this Function upon the receival of the Interrupt */
}

int main ( int argc, char **argv )
{
    int configfd;
    char buf[10];

    /* setup the signal handler for SIG_TEST 
     * SA_SIGINFO -> we want the signal handler function with 3 arguments
     */
    struct sigaction sig;

    sig.sa_sigaction = signalHandler;
    sig.sa_flags = SA_SIGINFO;
    sigaction(SIG_TEST, &sig, NULL);

    /* kernel needs to know our pid to be able to send us a signal ->
     * we use debugfs for this -> do not forget to mount the debugfs!
     */
    configfd = open("/sys/kernel/debug/signalconfpid", O_WRONLY);
    if(configfd < 0)
    {
        printf("Could not Open the File\n");
        perror("open");
        return -1;
    }

    sprintf(buf, "%i", getpid());               //! << Get the process ID.

    if (write(configfd, buf, strlen(buf) + 1) < 0)      //! << Write the details to Kernel Space.
    {
        perror("fwrite"); 
        return -1;
    }

    /*
     * Making the Application to run independently
     */
    while(RUNNING)
    {
        printf("Waiting for Interrup Signal...\n");
        sleep(1);
    }

    return 0;
}

signalHandler() — это функция на уровне вашего приложения, которая запускается, когда прерывание поступает к конкретному сконфигурированному GPIO. Теперь вы можете запускать свое приложение самостоятельно, а также сможете получать прерывания без опроса.

"Примеры говорят сами за себя, и для вашего понимания даны соответствующие комментарии"

Надеюсь это поможет.

14.03.2016
  • @SuneetTimes Я дал полное решение. Пожалуйста, просмотрите его и попытайтесь понять, оно решит вашу цель. Любой запрос всегда приветствуется. Пожалуйста, примите и подтвердите его, чтобы он мог помочь и другим. 14.03.2016
  • Хотя подход с использованием драйвера, вероятно, может сработать, он слишком сложен для платформы, которая, очевидно, уже имеет возможность прерывания в пространстве пользователя через интерфейс sysfs. 14.03.2016
  • Но это правильный способ сделать это. Вот как мы должны удалить метод непрерывного опроса из программы прикладного уровня. Опрос - это грубый способ и совсем не целесообразный. 14.03.2016
  • вы не читали документы GPIO Sysfs Interface for Userspace, не так ли? 14.03.2016
  • Но как вы избежите опроса. Да, я знаю об этом. Это URL [kernel.org/doc/Documentation/gpio/sysfs. txt], что объясняет это. Используя команды sysfs, в целях тестирования мы можем это сделать. Но для полной реализации приложения это нецелесообразно. 14.03.2016
  • @mfro Я вовсе не отрицаю точку зрения, которую вы хотите выдвинуть, но с точки зрения всей системы я пытаюсь выдвинуть свою точку зрения. 14.03.2016
  • Дело в том, что интерфейс sysfs предоставляет не только то, что вы можете опрашивать, но и интерфейс прерывания, где чтение будет блокироваться (не потребляя ЦП) до тех пор, пока условие не будет выполнено. Вам не нужно писать собственный драйвер ядра, если уже существует тот, который достигает цели. 15.06.2016
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..