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");
相关推荐
代码游侠23 分钟前
ARM开发——阶段问题综述(二)
运维·arm开发·笔记·单片机·嵌入式硬件·学习
DLGXY1 小时前
STM32——旋转编码器计次(七)
stm32·单片机·嵌入式硬件
羽获飞1 小时前
从零开始学嵌入式之STM32——3.使用寄存器点亮一盏LED灯
单片机·嵌入式硬件
浩子智控2 小时前
商业航天计算机抗辐射设计
单片机·嵌入式硬件
独处东汉5 小时前
freertos开发空气检测仪之输入子系统结构体设计
数据结构·人工智能·stm32·单片机·嵌入式硬件·算法
czy87874756 小时前
机智云 MCU OTA可以对MCU程序进行无线远程升级。
单片机·嵌入式硬件
A9better8 小时前
嵌入式开发学习日志52——二值与计数信号量
单片机·嵌入式硬件·学习
日更嵌入式的打工仔9 小时前
(实用向)中断服务程序(ISR)的优化方向
笔记·单片机
想放学的刺客10 小时前
单片机嵌入式试题(第25)嵌入式系统可靠性设计与外设驱动异常处理
stm32·单片机·嵌入式硬件·mcu·物联网
wotaifuzao10 小时前
STM32+FreeRTOS 长期可维护架构设计(事件驱动篇)-- 告别“屎山”代码
c语言·stm32·嵌入式硬件·freertos·状态机·事件驱动·嵌入式架构