linux——驱动——GPIO子系统

之前已经学习了对LED灯的驱动和对按健的驱动,而在Linux中实现这些设备驱动,有一种更为推荐的方法,就是GPIO子系统。

一、对GPIO子系统的介绍

GPIO(General Purpose Input/Output,通用输入输出)子系统是Linux内核中负责处理GPIO引脚的一个关键组件。它提供了一套接口,使得硬件工程师和软件开发者能够方便地使用和控制GPIO引脚,无论是初始化、设置引脚为输出并输出高低电平值,还是读取引脚的输入电平状态。的功能来与用户空间交互。例如控制LED、读取按键、触摸屏、鼠标都可以通过这些子系统接口实现。

二、主要功能

  • 初始化GPIO:在系统启动时或设备驱动加载时,GPIO子系统负责初始化GPIO控制器及其下的引脚。
  • 控制GPIO引脚:通过GPIO子系统提供的API,开发者可以设置引脚为输入或输出模式,并控制输出引脚的高低电平状态。
  • 读取GPIO引脚状态:对于输入引脚,GPIO子系统允许开发者读取其电平状态(高或低)。

2. 架构组成

Linux的GPIO子系统驱动框架主要由三个主要部分组成:

  • GPIO控制器驱动程序:这部分代码负责与具体的GPIO硬件控制器进行交互,执行硬件级的初始化和控制操作。
  • gpio lib驱动程序:作为中间层,提供了一套标准的API给上层使用,如设置引脚方向、读写引脚值等。
  • GPIO字符设备驱动程序:允许GPIO以字符设备的形式暴露给用户空间,用户空间程序可以通过标准的文件操作接口来访问GPIO。

三、具体实现

1、找到想要控制的引脚

所有的引脚在下图所在的文件中

2、用到的函数

1. gpio_request

函数原型

cs 复制代码
:int gpio_request(unsigned gpio, const char *label);

功能:请求控制指定的GPIO引脚。

  • gpio:要请求的GPIO引脚编号。
  • label:用于标识该GPIO引脚用途的标签字符串。

返回值 :成功时返回0,失败时返回负值错误代码(如 -EBUSY 表示该GPIO引脚已被请求或正在使用中)。

2. gpio_free

函数原型

cs 复制代码
void gpio_free(unsigned gpio);

功能 :释放之前通过 gpio_request 请求的GPIO引脚。

  • gpio:要释放的GPIO引脚编号。

3. gpio_direction_input

函数原型

cs 复制代码
int gpio_direction_input(unsigned gpio);

功能:设置指定的GPIO引脚为输入模式。

  • gpio:要设置为输入模式的GPIO引脚编号。

返回值:成功时返回0,失败时返回负值错误代码。

4. gpio_direction_output

函数原型

cs 复制代码
int gpio_direction_output(unsigned gpio, int value);

功能:设置指定的GPIO引脚为输出模式,并可选地设置其初始值。

  • gpio:要设置为输出模式的GPIO引脚编号。
  • value:设置为输出模式时的初始值(高电平或低电平)。

返回值:成功时返回0,失败时返回负值错误代码。

5. gpio_set_value

函数原型

cs 复制代码
void gpio_set_value(unsigned gpio, int value);

功能:设置指定GPIO引脚的输出值。

  • gpio:要设置输出值的GPIO引脚编号。
  • value:要设置的值(高电平或低电平)。

6. gpio_get_value

函数原型

cs 复制代码
int gpio_get_value(unsigned gpio);

功能:读取指定GPIO引脚的输入值。

  • gpio:要读取输入值的GPIO引脚编号。

返回值:读取到的值(高电平或低电平,通常表示为1或0)

四、源代码。

cs 复制代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/string.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <asm-generic/errno-base.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>

#define DEV_NAME "led"
#define PIN_LED S3C2410_GPB(5)

#define MAGIC_NUM_LED 'x'
#define LED_ON 1
#define LED_OFF 0

#define CMD_LED_ON _IO(MAGIC_NUM_LED, LED_ON)
#define CMD_LED_OFF _IO(MAGIC_NUM_LED, LED_OFF)

static void init_led(void)						
{
	// 配置GPB5引脚功能为输出, 将GPB5引脚电平置高
	gpio_request(PIN_LED, "led_pin");
	gpio_direction_output(PIN_LED, LED_OFF);
}

static void led_on(void)
{
	// 将GPB5引脚电平置低
	gpio_set_value(PIN_LED, LED_ON);
}

static void led_off(void)
{
	// 将GPB5引脚电平置高
	gpio_set_value(PIN_LED, LED_OFF);
}

static int open (struct inode * inode, struct file * file)
{
	init_led();
	printk("led open ...\n");
	return 0;
}

static ssize_t read (struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//copy_to_user(buf, data, len);
	printk("led read ...\n");
	return 0;
}

static ssize_t write (struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	unsigned char data[12] = {0};
	size_t len_cp = sizeof(data) < len ? sizeof(data) : len;
	copy_from_user(data, buf, len_cp);

	if(!strcmp(data, "ledon"))
		led_on();
	else if(!strcmp(data, "ledoff"))
		led_off();
	else
		 return -EINVAL;

	printk("led write ...\n");
	return len_cp;
}

static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
	//  cmd    type == 'x'   nr LED_ON   LED_OFF
	if(MAGIC_NUM_LED != _IOC_TYPE(cmd))
		return -EINVAL;

	if(LED_ON == _IOC_NR(cmd))
		led_on();
	else if(LED_OFF == _IOC_NR(cmd))
		led_off();

	return 0;
}

static int close (struct inode * inode, struct file * file)
{
	printk("led close ...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl,
	.release = close
};

static struct miscdevice misc = 
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static int __init led_init(void)
{
	int ret = misc_register(&misc);
	if(ret < 0)
		goto err_misc_register;

	printk("led_init  ...\n");
	return ret;

err_misc_register:
	misc_deregister(&misc);
	printk("led misc_register failed\n");	
	return ret;
}

static void __exit led_exit(void)
{
	misc_deregister(&misc);
	printk("led_exit  ###############################\n");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

五、类似的利用io口控制按健控制led灯

通过Linux内核的misc设备接口,实现了对指定GPIO引脚(用作按键输入)的状态读取,并将按键状态通过文件操作接口提供给用户空间。实现步骤包括:初始化GPIO引脚为输入、注册misc设备、实现文件操作函数(open、read、write、close)来管理GPIO读取。

cs 复制代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/string.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>

#define DEV_NAME "key"
#define PIN_KEY S3C2410_GPG(0)
#define LED_ON 0
#define LED_OFF 1

static void init_led(void)						
{

	gpio_request(PIN_KEY,"key_pin");
	gpio_direction_input(PIN_KEY);
}

static unsigned char get_key_status(void)
{
	return gpio_get_value(PIN_KEY);
}

static int open (struct inode *inode, struct file *file)
{
	init_led();
	printk("open..\n");
	return 0;
}

static ssize_t read (struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	int value = get_key_status();
	copy_to_user(buf,&value,sizeof(value));

	return 1;

}

static ssize_t write (struct file * file, const char __user * buf, size_t len, loff_t *offset)
{
	return 0;
}

static int close (struct inode * inode, struct file * file)
{
	printk("close..\n");
	return 0;

}
static struct file_operations fops =
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.release = close
};

//struct class *class;
//struct device *device;

static struct miscdevice misc_device_node = 
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static int __init led_init(void)
{
	int ret = 0;
//	class = class_create(THIS_MODULE,"led");
	ret = misc_register(&misc_device_node);
	if (ret < 0)
		goto err_misc_register;


//	device = device_create(class,NULL,dev,NULL,"led");
	printk("led_init ------------------------\n");

	return ret;

err_misc_register:
	misc_deregister(&misc_device_node);
	printk("led misc_register failed\n");
	return ret;
}

static void __exit led_exit(void)
{
//	device_destroy(class,dev);
//	class_destroy(class);
	misc_deregister(&misc_device_node);
	printk("led_exit ----------------------------\n");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
相关推荐
CV金科29 分钟前
蓝桥杯-STM32G431RBT6(串口)
c语言·stm32·单片机·嵌入式硬件·蓝桥杯
硬核科技2 小时前
变压器在电源中的核心作用
网络·单片机·嵌入式硬件·硬件工程·智能硬件·开关电源
CV金科3 小时前
蓝桥杯-STM32G431RBT6(UART解析字符串sscanf和解决串口BUG)
c语言·stm32·单片机·嵌入式硬件·mcu·算法·bug
培林将军3 小时前
C51单片机-单按键输入识别,键盘消抖
单片机·嵌入式硬件·计算机外设
hong1616884 小时前
单片机(Microcontroller)原理及应用
单片机·嵌入式硬件
Caihua_X4 小时前
ARM和51和stm32的区别
arm开发·stm32·嵌入式硬件
MGT_979614 小时前
ESP01的AT指令连接到阿里云平台
嵌入式硬件·物联网·阿里云·云计算
OH五星上将15 小时前
如何编译OpenHarmony SDK API
嵌入式硬件·移动开发·api·sdk·harmonyos·openharmony·鸿蒙开发
JT灬新一15 小时前
ARM驱动学习之 IOremap实现GPIO 读
arm开发·单片机·学习