i.MX7d процессор. Как написать драйвер под линукс для управления gpio.
Немного про i.MX7d. Все было бы очень хорошо, если бы не было так плохо. В определенных компаниях давно работают с продукцией Freescale и временами очень довольны. Достаточно интересный в своем составе чип из этой серии содержит на кристалле еще и М4 ядро. Все очень хорошо за исключением нескольких моментов.
Да, это супер, встроить M4 ядро рядом с ARMv7, даже не с одним, а двумя. Которые и содержит в себе вышеупомянутый чип i.MX7d процессора. Для STM32 процессоров написано много, временами даже очень много. Несмотря на то, что в нем так и не реализована правильная многозадачность на аппаратном уровне, а все возможные решение это чисто софт. Но фан клуб его много разработчиков объединил.
Достаточно лениво переходить на какой нибудь другой чип, если и так все ок. Вот и получается что огроомный плюс в его внутреннем М4 ядре. Народ любит такие чудеса.
Весьма знаменательно, что вместе с новым чипом ребята сделали и новую систему сборки вместо buildroot. Называется Yocto Project и его главная черта это надстройка из pyton над обычным gcc и Makefile. То есть ценность так себе, средненько.
Однако мы не привыкли отступать и в результате с удивлением обнаружили ядро версии 6.2 для этого процессора. Что удачно и собрали после некоторых ухищрений. Естественно захотелось большего и кроме стандартного buildroot взлетел и обычный ubuntu for arm.
Разработчик ( а писали ему письма крупным шрифтом ) и далеко не один раз, многократно повторял заученную фразу. В примерном переводе «я не знаю как управлять GPIO из ядра линукса, используйте M4 ядро» . Завелось и это. И как водится на одном из этапов появился некий HelloWorld для конкретной реализации на весьма конкретном процессоре i.MX7. Тем самым лишний раз доказав в том числе и разработчику, что в софте невозможного нет. В общем нет. Или совсем нет.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#define DEVICE_NAME "pbus_device"
#define CLASS_NAME "pbus"
#define GPIO3_BASE_ADDR 0x30230000
#define GPIO3_DR_OFFSET 0x00
#define GPIO3_GDIR_OFFSET 0x04
#define GPIO5_BASE_ADDR 0x30240000
#define GPIO5_DR_OFFSET 0x00
#define GPIO5_GDIR_OFFSET 0x04
#include "gpio_pins.h"
#include "imx7dea-com.h"
static int majorNumber;
static struct class* pbusClass = NULL;
static struct device* pbusDevice = NULL;
static void __iomem *gpio5_base;
static int dev_open(struct inode*, struct file*);
static int dev_release(struct inode*, struct file*);
static ssize_t dev_read(struct file*, char*, size_t, loff_t*);
static ssize_t dev_write(struct file*, const char*, size_t, loff_t*);
static void timer_callback(struct timer_list *t);
static struct file_operations fops =
{
.open = dev_open,
.release = dev_release,
.read = dev_read,
.write = dev_write,
};
static struct timer_list my_timer;
static void init_gpio(void)
{
if (!request_mem_region(GPIO5_BASE_ADDR, sizeof(u32), "gpio5")) {
pr_err("Failed to request GPIO5 memory region\n");
return;
}
gpio5_base = ioremap(GPIO5_BASE_ADDR, sizeof(u32));
if (!gpio5_base) {
pr_err("Failed to map GPIO5 base address\n");
release_mem_region(GPIO5_BASE_ADDR, sizeof(u32));
return;
}
// Инициализация gpioLed_U2
iowrite32(0, gpio5_base + GPIO5_DR_OFFSET); // Выключаем LED_U2
iowrite32((1 << 0), gpio5_base + GPIO5_GDIR_OFFSET); // Устанавливаем LED_U2 как выход
// Инициализация таймера
timer_setup(&my_timer, timer_callback, 0);
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
}
static void cleanup_gpio(void)
{
if (gpio5_base) {
iounmap(gpio5_base);
gpio5_base = NULL;
}
release_mem_region(GPIO5_BASE_ADDR, sizeof(u32));
}
static int __init pbus_init(void)
{
printk(KERN_INFO "pbus: Initializing the pbus module\n");
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber < 0)
{
printk(KERN_ALERT "pbus failed to register a major number\n");
return majorNumber;
}
pbusClass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(pbusClass))
{
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(pbusClass);
}
pbusDevice = device_create(pbusClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(pbusDevice))
{
class_destroy(pbusClass);
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(pbusDevice);
}
init_gpio(); // Инициализация GPIO
printk(KERN_INFO "pbus: Device created correctly\n");
return 0;
}
static void __exit pbus_exit(void)
{
del_timer_sync(&my_timer); // Удаляем таймер перед выходом
device_destroy(pbusClass, MKDEV(majorNumber, 0));
class_unregister(pbusClass);
class_destroy(pbusClass);
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_INFO "pbus: Goodbye from the pbus module\n");
cleanup_gpio(); // Очистка GPIO
}
static int dev_open(struct inode* inodep, struct file* filep)
{
printk(KERN_INFO "pbus: Device has been opened\n");
return 0;
}
static int dev_release(struct inode* inodep, struct file* filep)
{
printk(KERN_INFO "pbus: Device successfully closed\n");
return 0;
}
static ssize_t dev_read(struct file* filep, char* buffer, size_t len, loff_t* offset)
{
u32 gpio5_value;
if (!gpio5_base) {
printk(KERN_ALERT "GPIO not initialized\n");
return -EFAULT;
}
gpio5_value = ioread32(gpio5_base);
if (copy_to_user(buffer, &gpio5_value, sizeof(u32))) {
printk(KERN_ALERT "Failed to copy GPIO5 value to user\n");
return -EFAULT;
}
printk(KERN_INFO "pbus: Read GPIO5 register value: 0x%08x\n", gpio5_value);
return sizeof(u32);
}
static ssize_t dev_write(struct file* filep, const char* buffer, size_t len, loff_t* offset)
{
u32 gpio5_value;
if (!gpio5_base) {
printk(KERN_ALERT "GPIO not initialized\n");
return -EFAULT;
}
if (copy_from_user(&gpio5_value, buffer, sizeof(u32))) {
printk(KERN_ALERT "Failed to copy GPIO5 value from user\n");
return -EFAULT;
}
// Write to GPIO5 register
iowrite32(gpio5_value, gpio5_base);
printk(KERN_INFO "pbus: Wrote to GPIO5 register value: 0x%08x\n", gpio5_value);
return sizeof(u32);
}
static void timer_callback(struct timer_list *t)
{
static int led_state = 0;
if (gpio5_base) {
if (led_state == 0) {
iowrite32((1 << 0), gpio5_base + GPIO5_DR_OFFSET); // Зажигаем LED_U2
} else {
iowrite32(0, gpio5_base + GPIO5_DR_OFFSET); // Тушим LED_U2
}
led_state = 1 - led_state; // Переключаем состояние
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000)); // Перезапускаем таймер
}
}
module_init(pbus_init);
module_exit(pbus_exit);
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Linux driver for /dev/pbus");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
Постсткриптум i.MX7d
Врядл-ли он у Вас заработает прямо так сразу. Много но. И одно из этих «Но» это то, что шпаргалка теперь под рукой. Чем еще интересен этот малюсенький код, так это тем, что его сгенерировал обычный бесплатный искусственный интеллект после долгого и упорного объяснения что он должен сделать ,и некоторых других аспектов взаимодействия человека и железяки с алгоритмом.