Rockchip系列之客制化GPIO接口Driver部分(2)

简介: Rockchip系列之客制化GPIO接口Driver部分(2)

在这篇文章中,我将讨论如何在Rockchip 3568 11平台上创建自定义的GPIO驱动(理论上所有ARM平台都可以使用,无非就是dts或者driver gpio调用可能有一丢差异,但原理是一样的)。我将从设备树开始,然后深入到驱动的实现。

设备树配置

首先,我需要在设备树中定义我的自定义GPIO。这是我在xxx-evb3568-v1b-hdmi.dts文件中的配置:

custom_gpio: custom_gpio { 
    status = "okay";
    compatible = "custom,gpio";
    
    custom,gpios {
        custom,gpio0 {
            custom,gpio = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>;
            custom,config = <2>;  // 0: output(LOW) 1: output(HIGH) 2: input
        };
        custom,gpio1 {
            custom,gpio = <&gpio0 RK_PD4 GPIO_ACTIVE_HIGH>;
            custom,config = <2>;
        };
        custom,gpio2 {
            custom,gpio = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
            custom,config = <2>;
        };
        custom,gpio3 {
            custom,gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_HIGH>;
            custom,config = <2>;
        };
        custom,gpio4 {
            custom,gpio = <&gpio3 RK_PB3 GPIO_ACTIVE_HIGH>;
            custom,config = <0>;
        };
        custom,gpio5 {
            custom,gpio = <&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>;
            custom,config = <0>;
        };
        custom,gpio6 {
            custom,gpio = <&gpio3 RK_PB5 GPIO_ACTIVE_HIGH>;
            custom,config = <0>;
        };
        custom,gpio7 {
            custom,gpio = <&gpio3 RK_PB6 GPIO_ACTIVE_HIGH>;
            custom,config = <0>;
        };
    };
};

在这个配置中,我定义了一个名为custom_gpio的设备,它有8个GPIO,每个GPIO都有一个custom,gpio属性,用于指定GPIO的引脚和活动电平,以及一个custom,config属性,用于指定GPIO的初始配置。

驱动实现

接下来,我将看一下驱动的实现。驱动的主要部分在custom_gpio.c文件中。

首先,我们需要包含一些必要的头文件:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/signal.h>
#include <linux/pm.h>
#include <linux/notifier.h>
#include <linux/fb.h>
#include <linux/input.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>

定义了一些常量和数据结构:

#define CUSTOM_GPIO_NAME "custom,gpio"
#define CUSTOM_GPIO_MAX 20
#define CUSTOM_GPIO_DIRECTION_MASK 0x01
#define CUSTOM_GPIO_VALUE_MASK 0x02
// ioctl cmd
#define CUSTOM_GPIO_IOC_MAGIC  'f'
#define CUSTOM_GPIO_IOC_SET_VALUE _IOW(CUSTOM_GPIO_IOC_MAGIC, 1, int)
#define CUSTOM_GPIO_IOC_GET_VALUE _IOR(CUSTOM_GPIO_IOC_MAGIC, 2, int)
#define CUSTOM_GPIO_IOC_SET_DIRECTION _IOW(CUSTOM_GPIO_IOC_MAGIC, 3, int)
#define CUSTOM_GPIO_IOC_REG_KEY_EVENT _IOW(CUSTOM_GPIO_IOC_MAGIC, 4, int)
#define CUSTOM_GPIO_IOC_UNREG_KEY_EVENT _IOW(CUSTOM_GPIO_IOC_MAGIC, 5, int)
#define CUSTOM_GPIO_IOC_GET_NUMBER _IOW(CUSTOM_GPIO_IOC_MAGIC, 6, int)
#define CUSTOM_GPIO_IOC_TEST _IOW(CUSTOM_GPIO_IOC_MAGIC, 7, int)
#define CUSTOM_GPIO_IOC_MAXNR 7
#define CUSTOM_GPIO_CONFIG_OUTPUT_LOW 0
#define CUSTOM_GPIO_CONFIG_OUTPUT_HIGHT 1
#define CUSTOM_GPIO_CONFIG_INPUT 2
static int gKeyCode[CUSTOM_GPIO_MAX] = {
    KEY_GPIO_0,
    KEY_GPIO_1, 
    KEY_GPIO_2, 
    KEY_GPIO_3, 
    KEY_GPIO_4, 
    KEY_GPIO_5, 
    KEY_GPIO_6, 
    KEY_GPIO_7, 
    KEY_GPIO_8, 
    KEY_GPIO_9};
struct custom_gpio {
    int gpio;
    int config;
};
struct custom_gpio_data {
    struct platform_device *platform_dev;
    struct miscdevice custom_gpio_device;
    struct input_dev *input_dev;
    struct custom_gpio gpios[CUSTOM_GPIO_MAX];
    int irqs[CUSTOM_GPIO_MAX];
    int gpio_number;
};

这里,custom_gpio结构体用于存储每个GPIO的信息,custom_gpio_data结构体用于存储驱动的全局信息。

实现了一些辅助函数,用于操作GPIO:

static void custom_gpio_free_io_irq(struct custom_gpio_data *custom_gpio, int gpio)
{
    if (gpio >= 0 && gpio < custom_gpio->gpio_number) {
        if (custom_gpio->irqs[gpio] > 0) {
            free_irq(custom_gpio->irqs[gpio], custom_gpio);
            custom_gpio->irqs[gpio] = -1;
        }
    }
}
static void custom_gpio_free_irq(struct custom_gpio_data *custom_gpio)
{
    int i;
    for (i=0; i<custom_gpio->gpio_number; i++) {
        custom_gpio_free_io_irq(custom_gpio, i);
    }
}
static void custom_gpio_free_io_port(struct custom_gpio_data *custom_gpio)
{
    int i;
    for (i=0; i<custom_gpio->gpio_number; i++) {
        if(gpio_is_valid(custom_gpio->gpios[i].gpio)) {
            gpio_free(custom_gpio->gpios[i].gpio);
        }
    }
    return;
}
static int custom_gpio_parse_dt(struct device *dev,
                              struct custom_gpio_data *custom_gpio)
{
    int ret = 0, index = 0;
    struct device_node *np = dev->of_node;
    struct device_node *root  = of_get_child_by_name(np, "custom,gpios");
    struct device_node *child;
    for
_each_child_of_node(root, child) {
        if (index >= CUSTOM_GPIO_MAX) {
            dev_err(dev, "The number of GPIOs exceeds the maximum:%d value to break", CUSTOM_GPIO_MAX);
            break;
        }
        
        custom_gpio->gpios[index].gpio = of_get_named_gpio(child, "custom,gpio", 0);
        if(!gpio_is_valid(custom_gpio->gpios[index].gpio)) {
            dev_err(dev, "No valid gpio[%d]", index);
            return -1;
        }
        ret = of_property_read_u32(child, "custom,config", &custom_gpio->gpios[index].config);
        if(ret) {
            dev_warn(dev, "No valid gpio[%d]'s config, Use default values.", index);
            custom_gpio->gpios[index].config = CUSTOM_GPIO_CONFIG_INPUT;
        }
        index++;
    }
    custom_gpio->gpio_number = index;
    return 0;
}
static int custom_gpio_request_io_port(struct custom_gpio_data *custom_gpio)
{
    int ret = 0;
    int i;
    for (i=0; i<custom_gpio->gpio_number; i++) {
        if(gpio_is_valid(custom_gpio->gpios[i].gpio)) {
            ret = gpio_request(custom_gpio->gpios[i].gpio, "custom_gpio");
            if(ret < 0) {
                dev_err(&custom_gpio->platform_dev->dev,
                        "Failed to request GPIO[%d]:%d, ERRNO:%d\n",
                        i, (s32)custom_gpio->gpios[i].gpio, ret);
                return -ENODEV;
            }
            if (custom_gpio->gpios[i].config == CUSTOM_GPIO_CONFIG_INPUT) {
                gpio_direction_input(custom_gpio->gpios[i].gpio);
            } else if (custom_gpio->gpios[i].config == CUSTOM_GPIO_CONFIG_OUTPUT_LOW) {
                gpio_direction_output(custom_gpio->gpios[i].gpio, 0);
            } else if (custom_gpio->gpios[i].config == CUSTOM_GPIO_CONFIG_OUTPUT_HIGHT) {
                gpio_direction_output(custom_gpio->gpios[i].gpio, 1);
            } 
            
            dev_info(&custom_gpio->platform_dev->dev, "Success request gpio[%d]\n", i);
        }
    }
    return ret;
}
static s8 custom_gpio_request_input_dev(struct custom_gpio_data *custom_gpio)
{
    s8 ret = -1;
    custom_gpio->input_dev = input_allocate_device();
    if(custom_gpio->input_dev == NULL) {
        dev_err(&custom_gpio->platform_dev->dev, "Failed to allocate input device\n");
        return -ENOMEM;
    }
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_0);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_1);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_2);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_3);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_4);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_5);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_6);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_7);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_8);
    input_set_capability(custom_gpio->input_dev, EV_KEY, KEY_GPIO_9);
    ret = input_register_device(custom_gpio->input_dev);
    if(ret) {
        dev_err(&custom_gpio->platform_dev->dev, "Register %s input device failed\n",
                custom_gpio->input_dev->name);
        input_free_device(custom_gpio->input_dev);
        return -ENODEV;
    }
    return 0;
}
static int custom_gpio_set_value(int gpio, int value) {
    if(gpio_is_valid(gpio)) {
        gpio_set_value(gpio, value);
        return 0;
    }
    return -1;
}
static int custom_gpio_get_value(int gpio) {
    if(gpio_is_valid(gpio)) {
        return gpio_get_value(gpio);
    }
    return -1;
}
static int custom_gpio_set_direction(int gpio, int value) {
    int direction = 0;
    int data = 0;
    
    if(gpio_is_valid(gpio)) {
        direction = value & CUSTOM_GPIO_DIRECTION_MASK;
        data = value & CUSTOM_GPIO_VALUE_MASK;
        if (direction > 0) {
            return gpio_direction_output(gpio, data);
        } else {
            return gpio_direction_input(gpio);
        }
    }
    return -1;
}
static int custom_gpio_write(struct custom_gpio_data *custom_gpio, int gpio, int value) {
    int ret = -1;
    if (gpio < custom_gpio->gpio_number) {
       ret = custom_gpio_set_value(custom_gpio->gpios[gpio].gpio, value); 
    }
    return ret;
}
static int custom_gpio_read(struct custom_gpio_data *custom_gpio, int gpio) {
    int ret = -1;
    if (gpio < custom_gpio->gpio_number) {
       ret = custom_gpio_get_value(custom_gpio->gpios[gpio].gpio); 
    }
    return ret;
}
static int custom_gpio_direction(struct custom_gpio_data *custom_gpio, int gpio, int value) {
    int ret = -1;
    if (gpio < custom_gpio->gpio_number) {
       ret = custom_gpio_set_direction(custom_gpio->gpios[gpio].gpio, value); 
    }
    return ret;
}
static irqreturn_t custom_gpio_irq_handle(int irq, void *dev_id)
{
    struct custom_gpio_data *custom_gpio = dev_id;
    int gpio = -1;
    int value = -1;
    int i;
    for (i=0; i<custom_gpio->gpio_number; i++) {
        if (irq == custom_gpio->irqs[i]) {
            gpio = i;
        }
    }
    if (gpio >= 0 && gpio < custom_gpio->gpio_number) {
        value = custom_gpio_get_value(custom_gpio->gpios[gpio].gpio);
        if (value >= 0) {
           input_report_key(custom_gpio->input_dev, gKeyCode[gpio], !value);
           input_sync(custom_gpio->input_dev); 
        }
    }
    return IRQ_HANDLED;
}
static int custom_gpio_request_irq(struct custom_gpio_data *custom_gpio, int gpio)
{
    int ret = 0;
    /* use irq */
    if(gpio_is_valid(custom_gpio->gpios[gpio].gpio) || custom_gpio->irqs[gpio] > 0) {
        if(gpio_is_valid(custom_gpio->gpios[gpio].gpio))
            custom_gpio->irqs[gpio] = gpio_to_irq(custom_gpio->gpios[gpio].gpio);
        dev_info(&custom_gpio->platform_dev->dev, "INT num %d, trigger type:%d\n",
                 custom_gpio->irqs[gpio], IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING);
        ret = request_threaded_irq(custom_gpio->irqs[gpio], NULL,
                                   custom_gpio_irq_handle,
                                   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                  
 custom_gpio->platform_dev->name,
                                   custom_gpio);
        if(ret < 0) {
            dev_err(&custom_gpio->platform_dev->dev,
                    "Failed to request irq %d\n", custom_gpio->irqs[gpio]);
        }
    }
    return ret;
}
static int custom_gpio_dev_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    struct custom_gpio_data *custom_gpio = container_of(filp->private_data,
                               struct custom_gpio_data,
                               custom_gpio_device);
    filp->private_data = custom_gpio;
    dev_info(&custom_gpio->platform_dev->dev,
             "device node major=%d, minor=%d\n", imajor(inode), iminor(inode));
    return ret;
}
static long custom_gpio_dev_ioctl(struct file *pfile,
                     unsigned int cmd, unsigned long arg)
{
    int ret = 0;
    int i,j = 0;
    int data = 0;
    int gpio = 0, value = 0;
    struct custom_gpio_data *custom_gpio = pfile->private_data;
    if (_IOC_TYPE(cmd) != CUSTOM_GPIO_IOC_MAGIC) 
        return -EINVAL;
    if (_IOC_NR(cmd) > CUSTOM_GPIO_IOC_MAXNR) 
        return -EINVAL;
    if (_IOC_DIR(cmd) & _IOC_READ)
        ret = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        ret = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
    if (ret) 
        return -EFAULT;
    if (copy_from_user(&data, (int *)arg, sizeof(int))) {
        dev_err(&custom_gpio->platform_dev->dev, 
            "%s, copy from user failed\n", __func__);
        return -EFAULT;
    }
    gpio = (data >> 4) & 0x0f;
    value = data & 0x0f;
    
    dev_info(&custom_gpio->platform_dev->dev,
                 "%s, (%x, %lx): gpio=%d, value=%d\n", __func__, cmd,
                 arg, gpio, value);
    
    switch (cmd) {
        case CUSTOM_GPIO_IOC_SET_VALUE:
            ret = custom_gpio_write(custom_gpio, gpio, value);
            break;
        case CUSTOM_GPIO_IOC_TEST:
            for(j = 0;j<100;j++){
                for(i = 0;i<custom_gpio->gpio_number;i++){
                    if(j == 0){
                        custom_gpio_direction(custom_gpio,i, 1);
                    }else{
                        custom_gpio_write(custom_gpio, i, 1);
                    }
                }
                msleep(2000);
                for(i = 0;i< custom_gpio->gpio_number;i++){
                    custom_gpio_write(custom_gpio, i, 0);
                }
                msleep(2000);
            }
            break;
        case CUSTOM_GPIO_IOC_GET_VALUE:
            ret = custom_gpio_read(custom_gpio, gpio);
            if (ret >= 0) {
                if (copy_to_user((int *)arg, &ret, sizeof(int))) {
                    dev_err(&custom_gpio->platform_dev->dev, 
                        "%s, copy to user failed\n", __func__);
                    return -EFAULT;
                }
            } else {
                dev_err(&custom_gpio->platform_dev->dev, 
                        "%s, gpio get value failed\n", __func__);
                    return -EFAULT;
            }
            break;
        case CUSTOM_GPIO_IOC_SET_DIRECTION:
            ret = custom_gpio_direction(custom_gpio, gpio, value);
            break;
        case CUSTOM_GPIO_IOC_REG_KEY_EVENT:
            ret = custom_gpio_direction(custom_gpio, gpio, 0); // set input
            if (ret >= 0) {
                ret = custom_gpio_request_irq(custom_gpio, gpio);
            } else {
               dev_err(&custom_gpio->platform_dev->dev, 
                        "%s, reg key event set gpio input failed\n", __func__); 
            }
            break;
        case CUSTOM_GPIO_IOC_UNREG_KEY_EVENT:
            custom_gpio_free_io_irq(custom_gpio, gpio);
            break;
        case CUSTOM_GPIO_IOC_GET_NUMBER:
            if (copy_to_user((int *)arg, &custom_gpio->gpio_number, sizeof(int))) {
                dev_err(&custom_gpio->platform_dev->dev, 
                    "%s, copy to user failed\n", __func__);
                return -EFAULT;
            }
            break;
 
        default:
            return -EINVAL;
    }
    return ret;
}
static const struct file_operations custom_gpio_dev_fops = {
    .owner = THIS_MODULE,
    .open = custom_gpio_dev_open,
    .unlocked_ioctl = custom_gpio_dev_ioctl,
    .compat_ioctl = custom_gpio_dev_ioctl
};
static int custom_gpio_probe(struct platform_device *pdev)
{
    int ret = 0;
    struct custom_gpio_data *custom_gpio;
    printk("custom_gpio_probe\n");
    custom_gpio = devm_kzalloc(&pdev->dev, sizeof(*custom_gpio), GFP_KERNEL);
    if(custom_gpio == NULL) {
        dev_err(&pdev->dev, "Failed alloc ts memory");
        return -ENOMEM;
    }
    if(pdev->dev.of_node) {
        ret = custom_gpio_parse_dt(&pdev->dev, custom_gpio);
        if(ret) {
            dev_err(&pdev->dev, "Failed parse dts\n");
            goto exit_free_data;
        }
    }
    custom_gpio->platform_dev = pdev;
    ret = custom_gpio_request_io_port(custom_gpio);
    if(ret < 0) {
        dev_err(&pdev->dev, "Failed request IO port\n");
        goto exit_free_data;
    }
    ret = custom_gpio_request_input_dev(custom_gpio);
    if(ret < 0) {
        dev_err(&pdev->dev, "Failed request IO port\n");
        goto exit_free_io_port;
    }
    platform_set_drvdata(pdev, custom_gpio);
    custom_gpio->custom_gpio_device.minor = MISC_DYNAMIC_MINOR;
    custom_gpio->custom_gpio_device.name = "custom_gpio";
    custom_gpio->custom_gpio_device.fops = &custom_gpio_dev_fops;
    ret = misc_register(&custom_gpio->custom_gpio_device);
    if (ret) {
        dev_err(&pdev->dev, "Failed misc_register\n");
        goto exit_unreg_input_dev;
    }
    dev_info(&pdev->dev, "%s, over\n", __func__);
    return 0;
    
exit_unreg_input_dev:
    input_unregister_device(custom_gpio->input_dev);
exit_free_io_port:
    custom_gpio_free_io_port(custom_gpio);
    
exit_free_data:
    devm_kfree(&pdev->dev, custom_gpio);
    return ret;
}
static int custom_gpio_remove(struct platform_device *pdev)
{
    struct custom_gpio_data *custom_gpio = platform_get_drvdata(pdev);
    printk("custom_gpio_remove\n");
    custom_gpio_free_irq(custom_gpio);
    custom_gpio_free_io_port(custom_gpio);
    kfree(custom_gpio);
    return 0;
}
static const struct of_device_id custom_gpio_of_match[] = {
    { .compatible =  "custom,gpio"},
    {},
};
MODULE_DEVICE_TABLE(of, custom_gpio_of_match);
static struct platform_driver custom_gpio_driver = {
    .probe = custom_gpio_probe,
    .remove = custom
_gpio_remove,
    .driver = {
        .name = "custom_gpio",
        .owner = THIS_MODULE,
        .of_match_table = custom_gpio_of_match,
    },
};
module_platform_driver(custom_gpio_driver);
MODULE_AUTHOR("ln28");
MODULE_DESCRIPTION("Custom GPIO driver for Rockchip");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:custom-gpio");

这个驱动程序的主要功能是通过ioctl接口来控制GPIO的输入输出,同时也支持GPIO的中断,当GPIO的电平发生变化时,会生成一个键盘事件。

这个驱动程序在应用空间的使用方法如下:

  1. 打开设备文件:fd = open("/dev/custom_gpio", O_RDWR);
  2. 设置GPIO的值:ioctl(fd, CUSTOM_GPIO_IOC_SET_VALUE, (gpio << 4) | value);
  3. 获取GPIO的值:ioctl(fd, CUSTOM_GPIO_IOC_GET_VALUE, (gpio << 4));
  4. 设置GPIO的方向:ioctl(fd, CUSTOM_GPIO_IOC_SET_DIRECTION, (gpio << 4) | direction);
  5. 注册GPIO的键盘事件:ioctl(fd, CUSTOM_GPIO_IOC_REG_KEY_EVENT, (gpio << 4));
  6. 取消GPIO的键盘事件:ioctl(fd, CUSTOM_GPIO_IOC_UNREG_KEY_EVENT, (gpio << 4));
  7. 获取GPIO的数量:ioctl(fd, CUSTOM_GPIO_IOC_GET_NUMBER, &gpio_number);
  8. 关闭设备文件:close(fd);

你可以在Android系统源码中新建一个可执行程序通过调用ioctl接口方式来测试。

总结

本篇博客,介绍了如何在用户空间通过ioctl接口来控制GPIO的输入输出功能,以及如何注册GPIO中断来处理按键事件。定义了一些ioctl命令码和数据结构,以及实现了相应的操作函数和中断处理函数。这样就可以在用户空间通过/dev/custom_gpio设备文件来操作GPIO了。

相关文章
|
6月前
|
存储 Linux 开发工具
Rockchip系列之浅度分析UART接口系列(1)
Rockchip系列之浅度分析UART接口系列(1)
354 1
|
6月前
|
存储
Rockchip系列之浅度分析LED状态灯 Driver篇(1)
Rockchip系列之浅度分析LED状态灯 Driver篇(1)
124 2
|
6月前
|
Java Android开发
Rockchip系列之客制化GPIO接口应用部分(5)
Rockchip系列之客制化GPIO接口应用部分(5)
60 0
|
6月前
|
Linux API Android开发
Rockchip系列之客制化GPIO接口Hardware部分(3)
Rockchip系列之客制化GPIO接口Hardware部分(3)
87 0
|
6月前
|
安全 Java Android开发
Rockchip系列之客制化GPIO接口jni+service接口访问(4)
Rockchip系列之客制化GPIO接口jni+service接口访问(4)
58 0
|
Perl
PYNQ-关于PYNQ的GPIO的使用(RPI接口和arduino接口)或者常用的IO设备(如UART SPI IIC TIMER)
PYNQ-关于PYNQ的GPIO的使用(RPI接口和arduino接口)或者常用的IO设备(如UART SPI IIC TIMER)
636 0
PYNQ-关于PYNQ的GPIO的使用(RPI接口和arduino接口)或者常用的IO设备(如UART SPI IIC TIMER)
|
3月前
|
编解码 数据格式
IMX6ULL开发板spi OLED驱动
【8月更文挑战第24天】本文档介绍在IMX6ULL开发板上实现SPI接口OLED显示器驱动的步骤。首先需正确连接OLED至开发板的SPI接口,包括时钟(SCLK)、数据(MOSI)及片选(CS)等线路。理解SPI协议与OLED规格也很关键:SPI为同步串行通信,涉及主从设备交互;OLED参数如分辨率、颜色深度等须明确。接下来配置IMX6ULL的SPI控制器,通过设备树设定时钟频率、数据宽度等参数,并加载内核驱动。最后编写驱动程序,初始化SPI设备、发送控制命令与数据以完成OLED初始化,并实现文本或图像的显示功能。
|
API SoC
pinctrl和gpio子系统
pinctrl和gpio子系统
121 0
|
存储 Linux
uvc驱动中的v4l2
uvc驱动中的v4l2
136 0
|
Linux 芯片
GPIO和Pinctrl子系统的使用
GPIO和Pinctrl子系统的使用
95 0