驱动开发(3)|rk356x驱动GPIO基础应用之点亮led灯

点亮LED灯看似是一个基础的操作,但实际上,许多高级应用也依赖于高低电平的切换。例如,脉冲宽度调制(PWM)信号可以用来精确控制电机的转速,通过改变脉冲的频率和占空比,实现对电机的精确调节;同样,波形信号的生成也能够控制墨盒的喷墨精度,从而影响打印质量。通过对GPIO高低电平的精确控制,开发者可以实现各种复杂的硬件控制任务,这些基础操作为更复杂的应用提供了基础支持。

老规矩,先看效果:

这里控制led等一秒闪一次,下面分享两种控制led灯驱动代码:

1 通过GPIO 子系统设置引脚

c 复制代码
//led1.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>

#define DEVICE_NAME "led_clt"

#define IOCTL_POWER_ON      _IO('led', 1)
#define IOCTL_POWER_OFF     _IO('POWER_OFF', 0)

// 定义所有GPIO引脚 (根据实际硬件连接修改)
#define LED_PIN         101


static int power_gpios[] = {
    LED_PIN
};

static const char *power_gpio_names[] = {
    "LED"
};

void led_light(unsigned int epoch)
{
    int i = 0;
    for(i = 0 ;i < epoch; i++)
    {
        gpio_set_value(LED_PIN,1);
        mdelay(1000);
        gpio_set_value(LED_PIN,0);
        mdelay(1000);
    }

}
long gpio_user_ctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch (cmd) 
    {
        int i;
        case IOCTL_POWER_ON:
            led_light(5);
            break;
        case IOCTL_POWER_OFF:
            //下电时序信号

            printk(KERN_EMERG "inkjet power off...");
            break;


        default:
            return -ENOTTY;
    }
    return 0;
}

static const struct file_operations power_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = gpio_user_ctl,  // 设置ioctl回调
    .open = nonseekable_open,
};
static struct miscdevice gpio_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &power_fops,
};
// 初始化函数
static int __init power_init(void)
{
    int i, ret = 0;
    ret = register_chrdev(0, KERN_DEBUG, &power_fops);
    if (ret < 0) 
    {
        printk(KERN_ERR "Failed to register device\n");
        return ret;
    }
    
    // 获取设备号
    printk(KERN_INFO "Device registered with major number %d\n", ret);
    
    // 申请所有GPIO
    for (i = 0; i < ARRAY_SIZE(power_gpios); i++) 
    {
        ret = gpio_request(power_gpios[i], power_gpio_names[i]);
        if (ret)
        {
            printk(KERN_ERR "Failed to request GPIO %s (%d)\n", power_gpio_names[i], power_gpios[i]);
            goto error;
        }
        gpio_direction_output(power_gpios[i], 0);
    }
    //注册设备,用于内核与用户空间简单交互
    ret = misc_register(&gpio_dev);
    if (ret) 
    {
        goto error;
        printk(KERN_ERR "Failed to register misc device\n");
        return ret;
    }
    
    // 安装驱动
    printk(KERN_INFO "Motor Drive Install Success\n");
    

    return 0;

error:
    // 释放已申请的GPIO
    for (i = i - 1; i >= 0; i--) {
        gpio_free(power_gpios[i]);
    }
    return ret;
}

// 清理退出函数
static void __exit power_exit(void)
{
    int i;
    unregister_chrdev(0, DEVICE_NAME);  // 注销字符设备
    printk(KERN_INFO "Device unregistered\n");
    
    //释放所有GPIO
    for (i = ARRAY_SIZE(power_gpios) - 1; i >= 0; i--) {
        gpio_free(power_gpios[i]);
    }
    misc_deregister(&gpio_dev);
    
    printk(KERN_INFO "Motor Drive Uninstall Success\n");
}


module_init(power_init);
module_exit(power_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("limingzhao");
MODULE_DESCRIPTION("motor prj");

2 直接操作硬件寄存器

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/delay.h>
//msdevice
#include <linux/miscdevice.h>
#define DEV_NAME            "led_clt"

#define IOCTL_POWER_ON      _IO('led', 1)
#define IOCTL_POWER_OFF     _IO('POWER_OFF', 0)


#define GPIO1_COUNT            4
#define GPIO3_COUNT            11
#define GPIO4_COUNT            6

#define GROUP_COUNT            5

#define GPIO1_BASE (0xFE740000)
#define GPIO3_BASE (0xFE760000)
#define GPIO4_BASE (0xFE770000)

///A,B-->GPIO1_DR_L和GPIO1_DDR_L///C,D-->GPIO1_DR_H和GPIO1_DDR_H

//GPIO1A0,GPIO1A1,GPIO1B0,GPIO1B1-->GPIO1_DR_L和GPIO1_DDR_L
#define GPIO1_DR_L (GPIO1_BASE + 0x0000)
#define GPIO1_DDR_L (GPIO1_BASE + 0x0008)


//GPIO3_A0,GPIO3_A1...-->GPIO1_DR_L和GPIO1_DDR_L
#define GPIO3_DR_L (GPIO3_BASE + 0x0000)
#define GPIO3_DDR_L (GPIO3_BASE + 0x0008)

//GPIO4C3,GPIO4C5..-->GPIO2_DR_H和GPIO2_DDR_H
#define GPIO4_DR_H (GPIO4_BASE + 0x0004)
#define GPIO4_DDR_H (GPIO4_BASE + 0x000C)

static dev_t devno;
struct class *inkjet_chrdev_class;


typedef struct __PIN_INDEX{
	unsigned int  pin_index;
	unsigned long val_hig;
	unsigned long val_low;
}PIN_INDEX;

typedef struct __PIN_PARAMS{
    struct cdev dev;
	unsigned int 	__iomem *va_dr; 	// 数据寄存器,设置输出的电压
	unsigned int 	__iomem *va_ddr; 	// 数据方向寄存器,设置输入或者输出
	PIN_INDEX 		arrPin[20]; // 偏移
	unsigned int 	pin_count;

}PIN_PARAMS;

///GROUP1,GROUP3,GROUP4,The Number Of Array is Five for Simplifing Coding
static PIN_PARAMS GPIO_GROUPS[GROUP_COUNT];

void led_light(unsigned int epoch)
{
    int i = 0;
    for(i = 0 ;i < epoch; i++)
    {
        set_hig(2);
        mdelay(1000);
        set_low(2);
        mdelay(1000);
    }

}
long power_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch (cmd) 
    {
        int i;
        case IOCTL_POWER_ON:
			led_light(5);

            break;
        case IOCTL_POWER_OFF:

            break;

        default:
            return -ENOTTY;
    }
    return 0;
}
static struct file_operations inkjet_chrdev_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = power_ioctl,  // 设置ioctl回调
};
//注册设备信息,用于内核与用户空间简单交互
static struct miscdevice gpio_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEV_NAME,
    .fops = &inkjet_chrdev_fops,
};
void set_hig(unsigned int index)
{
			/*
	引脚对应图
		   pin					index
		GPIO1_A0 				 0
		GPIO1_A1				 1
		GPIO3_A5				 2
		GPIO3_A6				 3
		GPIO3_A7				 4
		GPIO4_C3				 5
		GPIO4_C5				 6
		GPIO4_C2				 7
		GPIO3_B4				 8
		GPIO4_D2				 9
		GPIO3_B6				 10
		GPIO3_B5				 11
		GPIO3_B7				 12
		GPIO3_B1				 13
		GPIO3_A0				 14
		GPIO4_C6				 15
		GPIO4_C4				 16
		GPIO3_B3				 17
		GPIO3_B2				 18
		GPIO1_B0				 19
		GPIO1_B1				 20

	*/
	if(index == 0)
	{	//GPIO1_A0
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[0].val_hig;
	}
	else if(index == 1)
	{	//GPIO1_A1
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[1].val_hig;
	}
	else if(index == 2)
	{	//GPIO3_A5
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[0].val_hig;

	}
	else if(index == 3)
	{	//GPIO3_A6
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[1].val_hig;

	}
	else if(index == 4)
	{	//GPIO3_A7
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[2].val_hig;
	}
	else if(index == 5)
	{	//GPIO4_C3
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[0].val_hig;
	}
	else if(index == 6)
	{	//GPIO4_C5
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[1].val_hig;
	}
	else if(index == 7)
	{	//GPIO4_C2
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[2].val_hig;
	}
	else if(index == 8)
	{	//GPIO3_B4
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[3].val_hig;
	}
	else if(index == 9)
	{	//GPIO4_D2
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[3].val_hig;
	}
	else if(index == 10)
	{	//GPIO3_B6
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[4].val_hig;
	}
	else if(index == 11)
	{	//GPIO3_B5
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[5].val_hig;
	}
	else if(index == 12)
	{	//GPIO3_B7
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[6].val_hig;
	}
	else if(index == 13)
	{	//GPIO3_B1
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[7].val_hig;
	}
	else if(index == 14)
	{	//GPIO3_A0
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[8].val_hig;
	}
	else if(index == 15)
	{	//GPIO4_C6
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[4].val_hig;
	}
	else if(index == 16)
	{	//GPIO4_C4
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[5].val_hig;
	}
	else if(index == 17)
	{	//GPIO3_B3
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[9].val_hig;
	}
	else if(index == 18)
	{	//GPIO3_B2
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[10].val_hig;
	}
	else if(index == 19)
	{	//GPIO1_B0
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[2].val_hig;
	}
	else if(index == 20)
	{	//GPIO1_B1
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[3].val_hig;
	}
	else
	{
		printk(KERN_EMERG "set_hig input index error!!\n");
	}


}
void set_low(unsigned int index)
{
				/*
	引脚对应图
		   pin					index
		GPIO1_A0 				 0
		GPIO1_A1				 1
		GPIO3_A5				 2
		GPIO3_A6				 3
		GPIO3_A7				 4
		GPIO4_C3				 5
		GPIO4_C5				 6
		GPIO4_C2				 7
		GPIO3_B4				 8
		GPIO4_D2				 9
		GPIO3_B6				 10
		GPIO3_B5				 11
		GPIO3_B7				 12
		GPIO3_B1				 13
		GPIO3_A0				 14
		GPIO4_C6				 15
		GPIO4_C4				 16
		GPIO3_B3				 17
		GPIO3_B2				 18
		GPIO1_B0				 19
		GPIO1_B1				 20

	*/

	if(index == 0)
	{	//GPIO1_A0
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[0].val_low;
	}
	else if(index == 1)
	{	//GPIO1_A1
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[1].val_low;
	}
	else if(index == 2)
	{	//GPIO3_A5
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[0].val_low;

	}
	else if(index == 3)
	{	//GPIO3_A6
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[1].val_low;

	}
	else if(index == 4)
	{	//GPIO3_A7
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[2].val_low;
	}
	else if(index == 5)
	{	//GPIO4_C3
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[0].val_low;
	}
	else if(index == 6)
	{	//GPIO4_C5
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[1].val_low;
	}
	else if(index == 7)
	{	//GPIO4_C2
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[2].val_low;
	}
	else if(index == 8)
	{	//GPIO3_B4
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[3].val_low;
	}
	else if(index == 9)
	{	//GPIO4_D2
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[3].val_low;
	}
	else if(index == 10)
	{	//GPIO3_B6
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[4].val_low;
	}
	else if(index == 11)
	{	//GPIO3_B5
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[5].val_low;
	}
	else if(index == 12)
	{	//GPIO3_B7
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[6].val_low;
	}
	else if(index == 13)
	{	//GPIO3_B1
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[7].val_low;
	}
	else if(index == 14)
	{	//GPIO3_A0
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[8].val_low;
	}
	else if(index == 15)
	{	//GPIO4_C6
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[4].val_low;
	}
	else if(index == 16)
	{	//GPIO4_C4
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[4].va_dr);
		*reg = GPIO_GROUPS[4].arrPin[5].val_low;
	}
	else if(index == 17)
	{	//GPIO3_B3
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[9].val_low;
	}
	else if(index == 18)
	{	//GPIO3_B2
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[3].va_dr);
		*reg = GPIO_GROUPS[3].arrPin[10].val_low;
	}
	else if(index == 19)
	{	//GPIO1_B0
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[2].val_low;
	}
	else if(index == 20)
	{	//GPIO1_B1
		volatile uint32_t *reg = (volatile uint32_t *)(GPIO_GROUPS[1].va_dr);
		*reg = GPIO_GROUPS[1].arrPin[3].val_low;
	}
	else
	{
		printk(KERN_EMERG "set_low input index error!!\n");
	}


}
void init_pin_values()
{
	int i = 0;
	int j = 0;
	
	dev_t cur_dev;
	unsigned long val = 0;

	
	////////GPIO1 GROUP SETTING//////
	GPIO_GROUPS[1].pin_count = GPIO1_COUNT;
	//GPIO1_A0
	GPIO_GROUPS[1].arrPin[0].pin_index = 0;
	//GPIO1_A1
	GPIO_GROUPS[1].arrPin[1].pin_index = 1;
	//GPIO1_B0
	GPIO_GROUPS[1].arrPin[2].pin_index = 8;
	//GPIO1_B1
	GPIO_GROUPS[1].arrPin[3].pin_index = 9;
	GPIO_GROUPS[1].va_dr = ioremap(GPIO1_DR_L, 4);
	GPIO_GROUPS[1].va_ddr = ioremap(GPIO1_DDR_L, 4);


	////////GPIO3 GROUP SETTING//////
	GPIO_GROUPS[3].pin_count = GPIO3_COUNT;
	//GPIO3_A5
	GPIO_GROUPS[3].arrPin[0].pin_index = 5;
	//GPIO3_A6
	GPIO_GROUPS[3].arrPin[1].pin_index = 6;
	//GPIO3_A7
	GPIO_GROUPS[3].arrPin[2].pin_index = 7;
	//GPIO3_B4
	GPIO_GROUPS[3].arrPin[3].pin_index = 12;
	//GPIO3_B6
	GPIO_GROUPS[3].arrPin[4].pin_index = 14;
	//GPIO3_B5
	GPIO_GROUPS[3].arrPin[5].pin_index = 13;
	//GPIO3_B7
	GPIO_GROUPS[3].arrPin[6].pin_index = 15;
	//GPIO3_B1
	GPIO_GROUPS[3].arrPin[7].pin_index = 9;
	//GPIO3_A0
	GPIO_GROUPS[3].arrPin[8].pin_index = 0;
	//GPIO3_B3
	GPIO_GROUPS[3].arrPin[9].pin_index = 11;
	//GPIO3_B2
	GPIO_GROUPS[3].arrPin[10].pin_index = 10;

	GPIO_GROUPS[3].va_dr = ioremap(GPIO3_DR_L, 4);
	GPIO_GROUPS[3].va_ddr = ioremap(GPIO3_DDR_L, 4);


	////////GPIO4 GROUP SETTING//////
	GPIO_GROUPS[4].pin_count = GPIO4_COUNT;
	//GPIO4_C3
	GPIO_GROUPS[4].arrPin[0].pin_index = 3;
	//GPIO4_C5
	GPIO_GROUPS[4].arrPin[1].pin_index = 5;
	//GPIO4_C2
	GPIO_GROUPS[4].arrPin[2].pin_index = 2;
	//GPIO4_D2
	GPIO_GROUPS[4].arrPin[3].pin_index = 10;
	//GPIO4_C6
	GPIO_GROUPS[4].arrPin[4].pin_index = 6;
	//GPIO4_C4
	GPIO_GROUPS[4].arrPin[5].pin_index = 4;
	GPIO_GROUPS[4].va_dr = ioremap(GPIO4_DR_H, 4);
	GPIO_GROUPS[4].va_ddr = ioremap(GPIO4_DDR_H, 4);

	alloc_chrdev_region(&devno, 0, GROUP_COUNT - 2, DEV_NAME);

	inkjet_chrdev_class = class_create(THIS_MODULE, "led_clt");

	for (; i < GROUP_COUNT; i++) 
	{
		if(i == 0|| i == 2)
		{
			continue;
		}
		cdev_init(&GPIO_GROUPS[i].dev, &inkjet_chrdev_fops);
		GPIO_GROUPS[i].dev.owner = THIS_MODULE;

		cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i);

		cdev_add(&GPIO_GROUPS[i].dev, cur_dev, 1);

		device_create(inkjet_chrdev_class, NULL, cur_dev, NULL,
			      DEV_NAME "%d", i);
	}

	// printk(KERN_EMERG "open\n");
	////////GPIO0 GROUP PINS EXPORT AND SAVE VALUE//////
	//五组GPIO(GPIO0-GPIO4)
	for(i = 0; i < GROUP_COUNT; i++)
	{
		if(i == 0|| i == 2)
		{
			continue;
		}
		for(j = 0; j < GPIO_GROUPS[i].pin_count; j++)
		{
			//export
			val = ioread32(GPIO_GROUPS[i].va_ddr);
			val |= ((unsigned int)0x1 << (GPIO_GROUPS[i].arrPin[j].pin_index+16));
			val |= ((unsigned int)0X1 << (GPIO_GROUPS[i].arrPin[j].pin_index));
			iowrite32(val, GPIO_GROUPS[i].va_ddr);

			//save hig and low value
			//high value
			val = ioread32(GPIO_GROUPS[i].va_dr);
			val |= ((unsigned int)0x1 << (GPIO_GROUPS[i].arrPin[j].pin_index+16));
			val &= ~((unsigned int)0x01 << (GPIO_GROUPS[i].arrPin[j].pin_index));   
			iowrite32(val, GPIO_GROUPS[i].va_dr);
			GPIO_GROUPS[i].arrPin[j].val_low = val;

			//low value
			val = ioread32(GPIO_GROUPS[i].va_dr);
			val |= ((unsigned int)0x1 << (GPIO_GROUPS[i].arrPin[j].pin_index+16));
			val |= ((unsigned int)0x1 << (GPIO_GROUPS[i].arrPin[j].pin_index));
			iowrite32(val, GPIO_GROUPS[i].va_dr);
			GPIO_GROUPS[i].arrPin[j].val_hig = val;

		}

	}

	int ret = 0;
	//注册设备,用于内核与用户空间简单交互
    ret = misc_register(&gpio_dev);
    if (ret) 
    {
        printk(KERN_ERR "Failed to register misc device\n");
        return;
    }
	

}



static __init int inkjet_chrdev_init(void)
{
	init_pin_values();

	return 0;
}

module_init(inkjet_chrdev_init);

static __exit void inkjet_chrdev_exit(void)
{
	int i;
	dev_t cur_dev;
	
	for (i = 0; i < GROUP_COUNT; i++) 
	{
		if(i == 0|| i == 2)
		{
			continue;
		}
		iounmap(GPIO_GROUPS[i].va_dr); 		// 释放模式寄存器虚拟地址
		iounmap(GPIO_GROUPS[i].va_ddr); 	// 释放输出类型寄存器虚拟地址
	}

	for (i = 0; i < GROUP_COUNT; i++) 
	{
		if(i == 0|| i == 2)
		{
			continue;
		}
		cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i);

		device_destroy(inkjet_chrdev_class, cur_dev);

		cdev_del(&GPIO_GROUPS[i].dev);

	}
	unregister_chrdev_region(devno, 1);
	class_destroy(inkjet_chrdev_class);
	//release access dev
	misc_deregister(&gpio_dev);
}

module_exit(inkjet_chrdev_exit);

MODULE_AUTHOR("limingzhao");
MODULE_LICENSE("GPL");

这里我嫌示例代码太麻烦了,自己封装了一下,set_higset_low,只封装了GPIO1、GPIO3和GPIO4,对应序号的gpio口在注释里面,方便使用。

3 makefile部分

bash 复制代码
KERNEL_DIR=/home/path/to/kernel/

ARCH=arm64
CROSS_COMPILE=aarch64-linux-gnu-
export  ARCH  CROSS_COMPILE

KBUILD_CFLAGS += -O0 -Wall 
obj-m := led1.o

all:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules

.PHONE:clean

clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean	

注:这里的KERNEL_DIR改成你自己编译的内核路径,如果不知道怎么编译的可以看我写的驱动开发(1)部分

这里的驱动代码和makefile放虚拟机里面(x86_64),编译成ko文件后放入驱动板里面安装,如图所示:

我这里的led1.c和led2.c分别对应本文段落1和本文段部落2的代码,这两种方法的优劣势上篇文章已经讲明白了。唯一的一点是上次纠正我上篇文章中的错误:上篇文章说通过直接操作寄存器的方式拉高再拉低,寄存器只能到达160ns左右是错误的,因为我之前用的示波器精度不够,这里直接操作寄存器的驱动代码完全能达到50ns级别的延时,不过我还是没用rk3568来直接操控,想要精确操控波形,感兴趣的可以研究一下FPGA开发板。

4 用户层代码

c 复制代码
//call_led.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>

// 定义IOCTL命令
#define IOCTL_POWER_ON      _IO('led', 1)
#define IOCTL_POWER_OFF     _IO('POWER_OFF', 0)

#define DEVICE_NAME "/dev/led_clt"

int main() 
{
    int fd;
    int ret;

    // 打开设备文件
    fd = open(DEVICE_NAME, O_RDWR);
    if (fd < 0) {
        perror("Failed to open device");
        return -1;
    }

    // 调用IOCTL_POWER_ON
    ret = ioctl(fd, IOCTL_POWER_ON);
    if (ret < 0) {
        perror("Failed to send IOCTL_POWER_ON");
        close(fd);
        return -1;
    }

    printf("Motor started\n");
    sleep(5);
    ret = ioctl(fd, IOCTL_POWER_ON);
    if (ret < 0) {
        perror("Failed to send IOCTL_POWER_ON");
        close(fd);
        return -1;
    }
    sleep(5);
    // 关闭设备文件
    close(fd);
    return 0;
}

这里是通过操作led_clt设备,来控制led灯电平高低,linux果然是万物皆文件,都是通过open、read、write基本操作函数。

这里可以看到 ret = ioctl(fd, IOCTL_POWER_ON);调用了两次,也就是led灯先闪烁5次,延时5秒然后再闪烁5次。

5 安装卸载驱动命令

bash 复制代码
# install
insmod led1.ko
# uninstall
rmmod led1.c

6 使用引脚

这里使用的引脚是GPIO3_A5,如图所示:

板子具体位置:

总结

这篇文章主要分享了在RK3568平台上控制GPIO的两种常见方式,并通过LED灯的实验测试,验证了这两种方法的效果。首先介绍了通过编程接口操作GPIO的基本原理和步骤,包括如何设置输入输出模式、读取和写入数据。后续这一系列将暂时停更,主要想先写CV和NLP算法方面的文章。

相关推荐
cxr8288 小时前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot16 小时前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶1 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot2 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot2 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday2 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot3 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot3 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8284 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday5 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发