Максимальная частота GPIO, которую удалось получить в предыдущей статье с помощью Python составила ~27 кГц. Надо сказать, такого быстродействия вполне достаточно для большинства задач, требующих частого переключения управляющего сигнала (например, управление динамической индикацией, вроде бегущих строк). Тем не менее, не исключены ситуации, когда от логических выводов Raspberry Pi может потребоваться большее быстродействие (ну например, чтобы принимать сигналы от пульта дистанционного управления нужно регистрировать сигналы с фото-датчика, несущая частота которого составляет 30-56 кГц). Как бы там ни было, интересно узнать предельные возможности GPIO и увидеть, насколько большой прирост производительности может дать C. Забегая вперед, скажу, что полученные результаты меня приятно удивили. Черновая редакция.
Исходное состояние
Исходное состояние мини-ПК такое же, как и в предыдущей статье: установлена и настроена ОС Raspbian, подключено два кабеля (питание и Ethernet), IP-адрес Raspberry - 192.168.1.35. Работа с Raspberry Pi также велась удаленно через терминал посредством SSH, хотя принципиального значения это не имеет. Как настроить SSH доступ описано тут. Все эксперименты проводились с тем же самым выводом общего назначения GPIO 7 (он же P1-26).
Лабораторная "установка" и оборудование
Для наблюдения сигнала на GPIO 7 использовался цифровой осциллограф Tektronix TDS1002B. Привожу фото осциллографа и всей установки (собственно, в предыдущей статье я использовал такое же оборудование, в прошлый раз я его не сфотографировал, поэтому выкладываю тут).
Примечание: Эксперименты проводились как с проводом (черно-красный на фото), так и без него (щупы подключены непосредственно к GPIO). Длина провода на полученные результаты не повлияла. Результаты измерений см. ниже.
Как работать с GPIO на C
Один из вариантов работы с GPIO на языке C - это использование библиотеки bcm2835, документацию и последнюю версию которой можно скачать здесь.
0) Скачиваем и устанавливаем библиотеку "bcm2835"
В терминале Raspberry Pi набираем:
wget www.open.com.au/mikem/bcm2835/bcm2835-1.21.tar.gz - скачиваем библиотеку
tar zxvf bcm2835-1.21.tar.gz - распаковываем архив, появится папка bcm2835-1.21/
cd bcm2835-1.xx - заходим в папку, компилируем и устанавливаем библиотеку:
./configure
make
sudo make check
sudo make install
cd bcm2835-1.xx - заходим в папку, компилируем и устанавливаем библиотеку:
./configure
make
sudo make check
sudo make install
Примечание: для последующей работы рекомендую использовать консольный файловый менеджер mc.
1)
Характеристики GPIO
1. Обычный режим
Для проверки максимальной скорости GPIO Raspberry использовался следующий код:
Исходный код
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <bcm2835.h>
#define PIN RPI_GPIO_P1_26
int main(int argc, char **argv, char **envp) {
int i;
if (!bcm2835_init())
exit(EXIT_FAILURE);
bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);
for(i = 0; i < 5000; i++) {
bcm2835_gpio_write(PIN, HIGH);
bcm2835_gpio_write(PIN, LOW);
}
bcm2835_close();
return (EXIT_SUCCESS);
}
Минимальная длительность импульса - 100 нс, частота переключения - 5 МГц!
2. Алгоритм планирования - SCHED_FIFO, sched_priority = 1
Исходный код
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <bcm2835.h>
#define PIN RPI_GPIO_P1_26
int main(int argc, char **argv, char **envp) {
int i;
struct sched_param sp;
if (!bcm2835_init())
exit(EXIT_FAILURE);
sp.sched_priority = 1;
if ( sched_setscheduler( 0, SCHED_FIFO, &sp ) !=0 )
fprintf(stderr, "%s:%i sched_setscheduler(): %s\n", __FILE__, __LINE__, strerror(errno));
bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);
for(i = 0; i < 5000; i++) {
bcm2835_gpio_write(PIN, HIGH);
bcm2835_gpio_write(PIN, LOW);
}
bcm2835_close();
return (EXIT_SUCCESS);
}
Результаты:
Результат - без изменений. Как и в первом случае, при многократном повторении эксперимента иногда возникают задержки (просадка на левом графике), причина которых - многозадачность Linux.
3. Алгоритм планирования - SCHED_FIFO, sched_priority = MAX
Исходный код
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <bcm2835.h>
#define PIN RPI_GPIO_P1_26
int main(int argc, char **argv, char **envp) {
int i;
struct sched_param sp;
if (!bcm2835_init())
exit(EXIT_FAILURE);
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
if ( sched_setscheduler( 0, SCHED_FIFO, &sp ) !=0 )
fprintf(stderr, "%s:%i sched_setscheduler(): %s\n", __FILE__, __LINE__, strerror(errno));
bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);
for(i = 0; i < 1000; i++) {
bcm2835_gpio_write(PIN, HIGH);
bcm2835_gpio_write(PIN, LOW);
}
bcm2835_close();
return (EXIT_SUCCESS);
}
Выводы
Сведем все результаты в общую таблицу:
Показатель |
Python (RPi.GPIO)
|
C
обычный режим
|
C
SCHED_FIFO, priority = 1
|
C
SCHED_FIFO, priority = MAX
|
Максимальная частота переключения |
27 кГц
|
5 МГц
|
5 МГц
|
5 МГц
|
Минимальная длительность импульса |
18-20 мкс
|
100 нс
|
100 нс
|
100 нс
|
Таким образом, по сравнению с Python'ом C позволил получить приблизительно 185-ти кратный прирост скорости. Максимальная частота переключения, которую можно программно "выжать" из логических выводов GPIO - 5 МГц (длительность импульса - 100 нс). При этом следует иметь ввиду следующее:
- добавление кода в цикл переключения может заметно увеличить длительность пребывания вывода в состояниях "1" и "0";
- полученные показатели могут быть нестабильны, поскольку Linux не является ОС реального времени.
Многократное повторение опытов показало, что поведение GPIO и появление "задержек" особо не зависит от алгоритма планирования и приоритета процесса. На данном этапе можно предположить, что положительный эффект от использования режима SCHED_FIFO может быть виден при большой загрузке процессора Raspberry, когда на нем выполняется ряд задач одновременно.
Поэтому, если через GPIO нужно управлять устройством, крайне критичным к режиму реального времени - таки придется использовать дополнительный микроконтроллер. Если же подключаемое устройство требует "скоростного" сигнала, но допускает появление задержек в несколько микросекунд - то вполне успешно можно использовать GPIO Raspberry без дополнительного контроллера.
PS. Благодарность анонимному комментатору из предыдущей статьи за идею с SCHED_FIFO и предоставленный пример кода.
Спасибо за статью! :)
ОтветитьУдалитьсобственно такие результаты я и ожидал, просто хотелось увидеть цифры.
вы правы, SCHED_FIFO будет играть роль когда CPU будет сильно подгружен.
sched_priority влиять и не должен, у вас нет RT процессов
а вот нестабильность сорее всего не из-за ОС, а что-то гораздо более низкоуровневое, возможно обработка прерываний
аноним. :)
Спасибо за исследование, цифры радуют)
ОтветитьУдалитьСкажите что надо установить, что-бы скомпилировать исходники, и как запустить потом на исполнение, или дайте ссылку где об этом почитать.
ОтветитьУдалитьСпасибо за статью.
Виталий.
Для компиляции ничего ставить не нужно - в Линуксе уже есть все необходимое. Компилятор - gcc. Если исходник написан в файле gpio.c, а исполняемый файл мы хотим назвать rasp_fifo, то строка для компиляции будет выглядеть так:
Удалитьgcc gpio.c -o rasp_fifo -l bcm2835
Буквально это значит следующее: запустить компилятор gcc, откомпилировать gpio.c и назвать исполняемый файл rasp_fifo, при компиляции использовать библиотеку bcm2835.
После этого появится исполняемый файл rasp_fifo, для его запуска:
sudo ./rasp_fifo
Можно раскрыть от чего программа на СИ получилась настолько "быстрой" и хотелось бы узнать, на сколько будет эффективней программа на си по отношению к аналогичной написанной в Python? Планируется написать программу с графической оболочкой для управления шаговыми двигателями по заложенному алгоритму. Возможно, если у Raspberry не хватит производительности, придется использовать ее как внешнее периферийного устройства, а программу ставить на основной PC.
ОтветитьУдалитьНу потому что Питон - это интерпретатор!
Удалитьспс !!
ОтветитьУдалитьочень интересно !!!
Как изменится скорость при отказе от ОС?
ОтветитьУдалитьПоможет ли повысить частоту использование ПДП