Программирование ARM LuckFox Pico GPIO Thu, September 18 2025  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


LuckFox Pico GPIO Печать
Добавил(а) microsin   

В этой статье (перевод документации [1]) описывается, как использовать файловую систему sysfs для получения доступа и управления портами GPIO (General-Purpose Input/Output).

[GPIO Subsystem]

GPIO это тип универсального вывода, который может управляться программно микроконтроллером (MCU) или CPU. Такой порт может выполнять различные функции ввода/вывода, включая детектирование уровней логического сигнала (при работе в режиме входа) или управления цифровыми устройствами (при работе в режиме вывода. На Linux выводы GPIO могут экспортироваться на рабочее пространство пользователя (user space) и управляться через файловую систему sysfs, благодаря чему выводы GPIO можно использовать для различных целей, таких как последовательный обмен данными, I2C, сетевые коммуникации, детектирование напряжения и других функций.

В Linux есть выделенная подсистема драйвера GPIO (GPIO subsystem driver framework) для работы с устройствами GPIO. Через эту подсистему можно просто работать с выводами GPIO через программирование CPU. Этот фреймворк драйвера поддерживает использование выводов для базовых функций ввода и вывода, а также поддерживает детектирование прерываний. Более детальную информацию о подсистеме GPIO можно найти в директории < Linux Kernel Source>/Documentation/gpio.

[GPIO Control (Shell)]

При экспортировании выводов GPIO в user space обычно нужны номера выводов. Эти номера можно определить по диаграммам интерфейса соответствующих плат. Например, имя вывода 4 платы будет GPIO1_C7_d, и соответствующий номер вывода 55.

Цоколевка выводов LuckFox Pico:

Pico pinout

Цоколевка выводов LuckFox Pico Mini A/B:

Pico Mini AB pinout

Цоколевка выводов LuckFox Pico Plus:

Pico Plus pinout

Цоколевка выводов LuckFox Pico Pro/Max:

Pico Pro Max pinout

Цоколевка выводов LuckFox Pico Ultra/Ultra W:

Pico Ultra Ultra W pinout

Вычисление номеров выводов. Номера выводов соответствующих портов GPIO помечены на картинках выше, и вы можете либо их использовать напрямую, либо вычислять их номера следующим образом:

• GPIO поделены на 5 банков: GPIO0 .. GPIO4, и у каждого банка есть 4 группы. В результате получается 32 вывода: A0 .. A7, B0 .. B7, C0 .. C7, D0 .. D7.

• GPIO именуются в формате GPIO{bank}_{group}{X}, как показано ниже:

GPIO0_A0 .. GPIO0_A7
GPIO0_B0 .. GPIO0_B7
GPIO0_C0 .. GPIO0_C7
GPIO0_D0 .. GPIO0_D7

GPIO1_A0 .. GPIO0_A7
...

GPIO1_D0 .. GPIO0_D7
...
GPIO4_D0 .. GPIO0_D7

• Номер вывода GPIO можно вычислить по следующей формуле:

pin = bank * 32 + number

Номер группы GPIO:

number = group * 8 + X

Таким образом:

pin = bank * 32 + (group * 8 + X)

В качестве примера рассмотрим GPIO1_C7_d, где:

bank: 1
group: 2 (A=0, B=1, C=2, D=3)
X: 7

Таким образом, номер порта GPIO1_C7_d будет: 1 x 32 + (2 x 8 + 7) = 55.

Директория устройства GPIO. В директории /sys/class/gpio каждое устройство GPIO имеет собственную папку. Имена папок содержат gpio (или gpiochip), за которым идет номер. Например, /sys/class/gpio/gpio55 представляет вывод номер 55, который именуется как GPIO1_C7_d.

Для проверки вы можете использовать команду:

[root@luckfox ]# ls /sys/class/gpio
export       gpiochip0    gpiochip128  gpiochip32   gpiochip96   unexport

Атрибуты устройства GPIO. В этих директориях устройств вы можете найти файлы управления, связанные выводами GPIO, включая направление (direction), значение (value) и прерывания, вместе с другими файлами. Каждая директория устройства GPIO содержит набор файлов атрибута, используемых для конфигурирования и управления выводами GPIO. Вы можете использовать следующие команды в директории устройства GPIO.

1. Просмотр файлов атрибута устройства GPIO 55:

# cd /sys/class/gpio
# echo 55 > /sys/class/gpio/export
# cd gpio55
# ls
value       power       subsystem   active_low
uevent      edge        device      direction

2. Файлы атрибутов. Используя файлы атрибутов, вы можете просто конфигурировать и управлять выводами GPIO, чтобы адаптировать их к различным сценариям применения и их требованиям. Некоторые ключевые атрибуты включают:

• Направление (direction): in для порта, сконфигурированного на вход, out для порта, сконфигурированного на выход.
• Значение (value): 0 для лог. 0, 1 для лог. 1.
• Перепад срабатывания прерывания (edge): rising для фронта нарастания уровня, falling для спада уровня, both для обоих перепадов, none для запрета прерывания.

Управление уровнями GPIO. Файлы атрибутов устройства работают как интерфейс для управления функциональными параметрами. Для каждой операции записи в файл /sys/class/gpio/gpio55/value происходит запуск кода драйвера, который модифицирует уровень на выходе GPIO pin 55 в соответствие со значением, записываемы в качестве параметра. При каждой операции чтения файла /sys/class/gpio/gpio55/value произойдет запуск кода драйвера для обновления текущего уровня GPIO pin 55 в соответствие с содержимым файла.

1. Экспорт GPIO 55 в user space:

# echo 55 > /sys/class/gpio/export

2. Чтение уровня GPIO1_C7_d:

# echo 55 > /sys/class/gpio/export
# echo in > /sys/class/gpio/gpio55/direction
# cat /sys/class/gpio/gpio55/value

3. Управление уровнем GPIO1_C7_d:

# echo out > /sys/class/gpio/gpio55/direction
# echo 1 > /sys/class/gpio/gpio55/value
# echo 0 > /sys/class/gpio/gpio55/value

4. Отмена экспорта GPIO 55 из user space:

# echo 55 > /sys/class/gpio/unexport

[Управление GPIO программой Python]

Следующая программа разрешает управление уровнями выводов GPIO и чтение их уровней.

from periphery import GPIO
import time
Write_Pin = 55 Read_Pin = 54
Write_GPIO = GPIO(Write_Pin, "out") Read_GPIO = GPIO(Read_Pin, "in")

try: while True: try: Write_GPIO.write(True) pin_state = Read_GPIO.read() print(f"Pin state: {pin_state}")
Write_GPIO.write(False) pin_state = Read_GPIO.read() print(f"Pin state: {pin_state}")
time.sleep(1)
except KeyboardInterrupt: Write_GPIO.write(False) break

except IOError: print("Error")

finally: Write_GPIO.close() Read_GPIO.close()

Конфигурирование направления GPIO. Следующий фрагмент кода использует библиотеку periphery, чтобы открыть два вывода GPIO в программе Python. Write_GPIO используется для вывода, и Read_GPIO для ввода.

Write_GPIO = GPIO(Write_Pin, "out")
Read_GPIO = GPIO(Read_Pin, "in")

Управление уровнем на выходе. Следующий фрагмент кода использует объект Write_GPIO для записи логического значения True в вывод GPIO, чтобы его выход перешел на уровень лог. 1. Запись логического значения False в вывод GPIO установит уровень на выходе в лог. 0.

Write_GPIO.write(True)
... Write_GPIO.write(False)

Чтение уровня вывода. Следующий код считывает и состояние вывода объекта Read_GPIO и печатает его. Read_GPIO.read() возвратит состояние вывода (True для лог. 1, False для лог. 0).

pin_state = Read_GPIO.read()
print(f"Pin state: {pin_state}")

Для редактирования кода программы используйте редактор vi или nano.

# nano gpio.py

Скопируйте код в буфер обмена, вставьте в редакторе и сохраните файл. Запустите программу командой:

# python3 gpio.py

[Управление GPIO программой C]

В предыдущих секциях было продемонстрировано управление GPIO с помощью команды echo и программы Python. Как обычно, вы можете использовать редактор vi или nano для модификации файлов, однако нужно обращать внимание на права пользователя при внесении изменений. Кроме того, мы можем использовать библиотечные C-функции или системные вызовы, чтобы читать и записывать файлы устройств для управления устройствами. Обратите внимание, что для запуска программ на определенных встраиваемых системах часто необходимо использовать инструменты кросс-компиляции, чтобы скомпилировать код и сгенерировать исполняемые файлы, которые можно запустить на определенной целевой плате. Давайте вместе рассмотрим определенные шаги по реализации.

1. Следующая программа может управлять выводами ножек портов GPIO.

#include < stdio.h>
#include < stdlib.h>
#include < unistd.h>

int main() {
int gpio_pin;

printf("Please enter the GPIO pin number: ");
scanf("%d", &gpio_pin);

FILE *export_file = fopen("/sys/class/gpio/export", "w");
if (export_file == NULL) {
perror("Failed to open GPIO export file");
return -1;
}
fprintf(export_file, "%d", gpio_pin);
fclose(export_file);

char direction_path[50];
snprintf(direction_path, sizeof(direction_path),
"/sys/class/gpio/gpio%d/direction", gpio_pin);
FILE *direction_file = fopen(direction_path, "w");
if (direction_file == NULL) {
perror("Failed to open GPIO direction file");
return -1;
}
fprintf(direction_file, "out");
fclose(direction_file);

char value_path[50];
char cat_command[100];
snprintf(value_path, sizeof(value_path),
"/sys/class/gpio/gpio%d/value", gpio_pin);
snprintf(cat_command, sizeof(cat_command), "cat %s", value_path);
FILE *value_file = fopen(value_path, "w");
if (value_file == NULL) {
perror("Failed to open GPIO value file");
return -1;
}

for (int i = 0; i < 3; i++) {
fprintf(value_file, "1");
fflush(value_file);

system(cat_command);
sleep(1);

fprintf(value_file, "0");
fflush(value_file);

system(cat_command);
sleep(1);
}

fclose(value_file);
FILE *unexport_file = fopen("/sys/class/gpio/unexport", "w");
if (unexport_file == NULL) {
perror("Failed to open GPIO unexport file");
return -1;
}
fprintf(unexport_file, "%d", gpio_pin);
fclose(unexport_file);

return 0; }

Рассмотрим эту программу подробнее.

Экспорт вывода в User Space. Следующий фрагмент кода считает введенный пользователем номер вывода, откроет файл /sys/class/gpio/export и запишет в него это число, благодаря чему произойдет операция экспорта для вывода GPIO.

printf("Please enter the GPIO pin number: ");
scanf("%d", &gpio_pin);

FILE *export_file = fopen("/sys/class/gpio/export", "w");
if (export_file == NULL) {
perror("Failed to open GPIO export file");
return -1; } fprintf(export_file, "%d", gpio_pin); fclose(export_file);

Конфигурирование направления GPIO. Следующий фрагмент кода откроет файл /sys/class/gpio/gpiox/direction и запишет в него "out", чтобы установить этот вывод GPIO в режим выхода. Если вы хотите сконфигурировать его как вход, то нужно записать вместо этого "in".

char direction_path[50];
snprintf(direction_path, sizeof(direction_path),
"/sys/class/gpio/gpio%d/direction", gpio_pin);
FILE *direction_file = fopen(direction_path, "w");
if (direction_file == NULL) {
perror("Failed to open GPIO direction file");
return -1; } fprintf(direction_file, "out"); fclose(direction_file);

Управление уровнем выхода GPIO. Этот код используется для управления выходным уровнем логики вывода GPIO. Сначала вам нужно открыть файл /sys/class/gpio/gpiox/value, который используется для установки уровня на выводе GPIO. В цикле мы управляем выходом, записывая в этот файл последовательно "1" и "0". Затем мы используем команду cat для проверки, что значение, сохраненное в файл, было успешно записано.

Обратите внимание, что стандартная библиотека C обычно использует буферы для повышения эффективности операций с файлами, вместо того, чтобы немедленно записывать данные в файл при каждой операции записи. Это значит, что даже если вы используете fprintf для записи данных, то на самом деле данные могут просто попадать в буфер вместо того, чтобы немедленно записываться в файл. Если вы хотите, чтобы эти данные были немедленно записаны в файл, то можете использовать функцию fflush для "слива" буфера, что заставит данные буфера записаться в файл, либо можете использовать способ последовательных операций fopen -> fprintf -> fclose для каждой записи.

char value_path[50];
char cat_command[100]; snprintf(value_path, sizeof(value_path),
"/sys/class/gpio/gpio%d/value", gpio_pin); snprintf(cat_command, sizeof(cat_command),
"cat %s", value_path);
FILE *value_file = fopen(value_path, "w");
if (value_file == NULL) {
perror("Failed to open GPIO value file");
return -1; }

for (int i = 0; i < 3; i++) {
fprintf(value_file, "1");
fflush(value_file);

system(cat_command);
sleep(1);

fprintf(value_file, "0");
fflush(value_file);

system(cat_command);
sleep(1); }
fclose(value_file);

Отмена экспорта вывода. Следующий код откроет файл /sys/class/gpio/unexport и запишет в него номер порта, чем достигается отмена операции экспорта для этого вывода GPIO.

FILE *unexport_file = fopen("/sys/class/gpio/unexport", "w");
if (unexport_file == NULL) {
perror("Failed to open GPIO unexport file");
return -1; } fprintf(unexport_file, "%d", gpio_pin); fclose(unexport_file);

[Кросс-компиляция программы C]

1. Укажите Cross-Compilation Tool. Сначала вам нужно добавить путь до утилит кросс-компиляции (cross-compilation) в системную переменную PATH, чтобы можно было запускать утилиты кросс-компиляции из любого места. Для этого вы можете добавить следующую строку в свой файл конфигурации шелла (обычно это файл ~/.bashrc, или ~/.bash_profile, или ~/.zshrc, в зависимости от вашего шелла). Обратите внимание, что путь после PATH= должен указывать на директорию, где находится исполняемый файл кросс-компилятора arm-rockchip830-linux-uclibcgnueabihf-gcc. Этот файл должен находиться в следующей папке:

< директория SDK>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/

Откройте в редакторе текста конфигурационный файл шелла:

$ vi ~/.bashrc

Добавьте путь до утилиты кросс-компилятора в системную переменную PATH так, чтобы он был первым, сразу после знака равно. При этом замените < SDK Directory> на реальный путь вашего SDK, такой как /home/luckfox/luckfox-pico/.

export PATH=< SDK Directory>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:$PATH

Перезагрузите файл конфигурации для применения изменений:

$ source ~/.bashrc

2. Скомпилируйте программу с помощью кросс-компилятора:

$ arm-rockchip830-linux-uclibcgnueabihf-gcc gpio.c -o gpio

3. После успешной компиляции в текущей директории появится файл gpio, который может быть запущен на целевой плате.

$ ls
gpio  gpio.c

4. Чтобы можно было запустить скомпилированную программу на целевой плате, необходимо её скопировать в файловую систему платы. Для этого можно использовать SCP, TFTP или ADB.

Вот так это можно сделать с помощью ADB. Следующая команда копирует исполняемый файл gpio в каталог /oem/gpiotest на целевой системе:

$ adb push gpio /oem/gpiotest

5. Для запуска программы запустите шелл платы, например с помощью ADB:

$ adb shell
[root@luckfox ~]# cd /oem/gpiotest
[root@luckfox gpiotest]# ls
gpio

Измените права доступа на файл gpio, чтобы он стал исполняемым:

[root@luckfox gpiotest]# chmod +x gpio

Следующая команда запустит нашу тестовую программу:

[root@luckfox gpiotest]# ./gpio
Please enter the GPIO pin number: 55
1
0
1
0
1

[Модификация Device Tree]

Выводы 55 и 54 в программе можно использовать напрямую без модификации device tree. Если нам нужно сконфигурировать другие выводы как general-purpose IO, то нужно изменить device tree.

1. Модификация файла Device Tree. Файлы конфигурации *.mk устройств разных поддерживаемых плат SDK находятся в папке < SDK directory>/project/cfg/BoardConfig_IPC:

luckfox-pico$ ls -1 project/cfg/BoardConfig_IPC
BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_86Panel-IPC.mk BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_86Panel_W-IPC.mk BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_Pi-IPC.mk BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_Pi_W-IPC.mk BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_Ultra-IPC.mk BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_Ultra_W-IPC.mk BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_Zero-IPC.mk BoardConfig-EMMC-Busybox-RV1106_Luckfox_Pico_Ultra-IPC_FASTBOOT.mk BoardConfig-SD_CARD-Buildroot-RV1103_Luckfox_Pico-IPC.mk BoardConfig-SD_CARD-Buildroot-RV1103_Luckfox_Pico_Mini-IPC.mk BoardConfig-SD_CARD-Buildroot-RV1103_Luckfox_Pico_Plus-IPC.mk BoardConfig-SD_CARD-Buildroot-RV1103_Luckfox_Pico_WebBee-IPC.mk BoardConfig-SD_CARD-Buildroot-RV1106_Luckfox_Pico_Pro_Max-IPC.mk BoardConfig-SPI_NAND-Buildroot-RV1103_Luckfox_Pico_Mini-IPC.mk BoardConfig-SPI_NAND-Buildroot-RV1103_Luckfox_Pico_Plus-IPC.mk BoardConfig-SPI_NAND-Buildroot-RV1103_Luckfox_Pico_WebBee-IPC.mk BoardConfig-SPI_NAND-Buildroot-RV1106_Luckfox_Pico_Pro_Max-IPC.mk BoardConfig-SPI_NAND-Busybox-RV1106_Luckfox_Pico_Pro_Max-IPC_FASTBOOT.mk luckfox-buildroot-nocsi-oem-pre.sh luckfox-buildroot-oem-pre.sh luckfox-glibc-oem-pre.sh luckfox-rv1106-tb-emmc-post.sh luckfox-rv1106-tb-spi_nand-post.sh luckfox-systemd-off-modem-post.sh luckfox-userdata-pre.sh
overlay

Эти файлы содержат главным образом конфигурационные параметры разных моделей плат Luckfox Pico, настраивая такие их аспекты, как целевая архитектура (target architecture), загрузочный носитель (boot medium), конфигурации загрузчика (Uboot) и ядра (kernel), настройки разделов (partition settings). Структура директорий SDK следующая:

├── build.sh -> project/build.sh ---- скрипт компиляции SDK
├── media --------------------------- кодирование Multimedia, ISP и связанные алгоритмы
|                                     (может компилироваться независимо в SDK)
├── sysdrv -------------------------- директория U-Boot, kernel, rootfs (может
|                                     компилироваться независимо в SDK)
├── project ------------------------- образцовые приложения, конфигурации компиляции
|   |                                 и директории скриптов
│   ├── cfg
│       ├── BoardConfig_IPC
│           ├── BoardConfig-EMMC-NONE-RV1103_Luckfox_Pico-IPC.mk            
│           ├── BoardConfig-EMMC-NONE-RV1103_Luckfox_Pico_Mini_A-IPC.mk      
│           ├── BoardConfig-SPI_NAND-NONE-RV1103_Luckfox_Pico_Mini_B-IPC.mk
│           ├── BoardConfig-SPI_NAND-NONE-RV1103_Luckfox_Pico_Plus-IPC.mk
│           ├── BoardConfig-SPI_NAND-NONE-RV1106_Luckfox_Pico_Pro_Max-IPC.mk
│           └── ...
├── output -------------------------- директория для сохранения скомпилированных
|                                     файлов образа SDK
├── docs ---------------------------- директория документации SDK
└── tools --------------------------- утилиты упаковки образа (packaging tools)
                                      и прошивки (burning tools)

Экспортируемая переменная окружения RK_KERNEL_DTS указывает файл исходника для дерева устройств (Device Tree Source, DTS), который использует ядро (kernel). Если взять в качестве примера плату Luckfox Pico и открыть её файл конфигурации BoardConfig-EMMC-NONE-RV1103_Luckfox_Pico-IPC.mk, то мы можем увидеть, что переменная окружения RK_KERNEL_DTS указывает на файл rv1103g-luckfox-pico.dts (находится в каталоге sysdrv/source/kernel/arch/arm/boot/dts корня SDK).

dts1

2. Определение GPIO. Обычно определение GPIO требует добавления двух секций кода. Обратите внимание, что кнопка опрашивается по активному уровню лог. 0, и вход должен иметь подтяжку к верхнему уровню (pull-up). Ниже показан пример, как добавить определение вывода GPIO1_C7_d в device tree.

dts2

dts3

Добавляемые куски кода:

/{
gpio1pc7:gpio1pc7 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = < &gpio1_pc7>;
regulator-name = "gpio1_pc7";
regulator-always-on;
}; };

&pinctrl {
gpio1-pc7 {
gpio1_pc7:gpio1-pc7 {
rockchip,pins = < 1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
};
}; };

3. Комментирование функции вывода периферийного устройства. Запрет функции периферийного устройства вывода достигается комментированием соответствующего узла конфигурации в файле device tree. Ниже показан пример, как запретить функцию ШИМ (PWM) на выводе GPIO1_C7_d.

4. Компиляция ядра. SDK компилируется путем выбора ветвей для LuckFox Pico, LuckFox Pico Mini A, LuckFox Pico Mini B, LuckFox Pico Plus и LuckFox Pico Pro/Max. Выполните команду, и в ответ на запрос выбора платы введите её номер в меню:

~/luckfox-pico$ ./build.sh lunch

5. Рекомпиляция ядра:

~/luckfox-pico$ ./build.sh kernel

[Повторная прошивка firmware]

1. После успешной компиляции kernel сгенерированные файлы можно найти в директории < SDK directory>/output/image.

$ ls output/image/
boot.img  download.bin  env.img  idblock.img  oem.img  rootfs.img  sd_update.txt
 tftp_update.txt  uboot.img  update.img  userdata.img

2. Для загрузки с карты заново создайте карту SD. Для загрузки из SPI NAND FLASH используйте команду прошивки:

$ cd output/image/
$ sudo upgrade_tool uf update.img

[Ссылки]

1. LuckFox Pico GPIO site:wiki.luckfox.com.

 

Добавить комментарий


Защитный код
Обновить

Top of Page