Linux内核编程(三)gpio库函数驱动编写


  

  Linux 对于 gpio 口常用操作,提供了一套很便捷的驱动 API, 开发者不需要自己去映射寄存器,只需要调用内核提供的标准 API 函数即可完成对 gpio 口的方向配置,电平设置,状态读取,转换为外部中断编号等操作。

一、gpio库函数API接口

1. 读取 gpio 电平状态

int gpio_get_value(unsigned int gpio);
返回值: 成功:0/1     失败:<0

2. 设置gpio电 平状态

void gpio_set_value(unsigned int gpio, int value);

3. 申请gpio

int gpio_request(unsigned gpio, const char *label);
返回值: 成功:0    失败:<0

4. 释放gpio

void gpio_free(unsigned gpio);

5. 配置gpio输入方向

int gpio_direction_input(unsigned gpio);
返回值: 成功: 0     失败:<0

6. 配置gpio输出方向

int gpio_direction_output(unsigned gpio, int value);
返回值: 成功: 0     失败:<0

7. 申请一个gpio

int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
返回值: 成功:    失败:<0

8. 申请一组gpio

int gpio_request_array(const struct gpio *array, size_t num);
返回值: 成功:     失败:<0

9. 释放一组gpio

void gpio_free_array(const struct gpio *array, size_t num);

10. gpio 编号转换外部中断编号

int gpio_to_irq(unsigned gpio);
返回值: 成功:>=0     失败:<0

11. 外部中断编号转为gpio

int irq_to_gpio(unsigned irq);
返回值: 成功:>=0     失败:<0

二、使用gpio流程

  1. 申请 GPIO。
  2. 配置为方向:LED 配置为输出方向。
  3. 控制电平输出或读取电平状态。
  4. 释放 GPIO。

三、gpio模块程序各函数实现功能

1. 模块初始化函数: 申请杂项设备、 GPIO。将GPIO置为输出方向,输出高电平,让 LED 处于灭状态。
2. 模块的卸载函数: 释放 GPIO与注销杂项设备。
3. 文件操作方法的 write 函数: 控制 gpio 电平输出。
4. 文件操作方法的 read 函数: 打印“ hello!” 。
   

四、实现写功能示例一(库函数申请一个gpio)

功能:蜂鸣器响3s—打印“ hello!”(将蜂鸣器接到序号为36的GPIO引脚,低电平触发)
(1)gpio.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/sys_config.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>

#define BEEP	    36   //这里为GPIO的序号
#define KBUF_SIZE (1024)
#define MIN(x, y) ((x)<(y)?(x):(y))
char kbuf[KBUF_SIZE];  //存储用户写入的数据。
char *rbuf="hello!";   //用户读取的数据

int gpio_open (struct inode *node, struct file *fp)
{
	return 0;
}
int gpio_release (struct inode *node, struct file *fp)
{
	return 0;
}

ssize_t gpio_read (struct file *fp, char __user *ubuff, size_t size, loff_t *offset)
{  
    int ret;
	ret=copy_to_user(ubuff, rbuf, strlen(rbuf));
	if(ret != 0) {
		pr_err("copy_to_user error\r\n");
		return -EINVAL;
	}
	return strlen(rbuf);
}

ssize_t gpio_write(struct file *fp, const char __user *ubuf, size_t size, loff_t *offset)
{
	int ret;
	memset(kbuf, 0, sizeof(kbuf));
	ret = copy_from_user(kbuf, ubuf, MIN(KBUF_SIZE, size));
	if(ret != 0) {
		pr_err("copy_from_user error\r\n");
		return -EINVAL;
	}	
	if( strstr(kbuf, "BEEP_ON") )      gpio_set_value(BEEP	, 0);   
    else if(strstr(kbuf, "BEEP_OFF"))  gpio_set_value(BEEP	, 1);
	
	return MIN(KBUF_SIZE, size);  //返回写入的字节数
}

const struct file_operations gpio_fops={
    .owner=THIS_MODULE,     //文件操作集的拥有者是 本模块。
    .read= gpio_read,
  	.write= gpio_write,
  	.open= gpio_open,
  	.release= gpio_release,
};

struct miscdevice mygpio={
	.minor=MISC_DYNAMIC_MINOR,    //系统自动分配次设备号
	.name ="my_gpio",
	.fops=&gpio_fops,
};

static int __init gpio_init(void)
{
  int ret;
  ret=misc_register(&mygpio);
  if(ret!=0){
     pr_err("misc_register error");
	 return -EINVAL;   //返回错误编码
  }
  ret = gpio_request(BEEP, "BEEP");
  if(ret != 0) {
		pr_err("gpio_request error\r\n");
		return -1;
	}  
 //配置gpio的方向、无效电平
   gpio_direction_output(BEEP, 1);   
   return 0;
}

static void __exit gpio_exit(void)
{
 gpio_free(BEEP);  //释放一个gpio
 misc_deregister(&mygpio);  //从内核注销一个杂项设备
}

module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");

(2)app.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **arg)
{
	int fd;
	char data[64];

	fd = open("/dev/my_gpio", O_RDWR);
	if(fd < 0) {
		perror("open error");
		return -1;
	}

	write(fd, "BEEP_ON", sizeof("BEEP_ON"));
	sleep(3);
	write(fd, "BEEP_OFF", sizeof("BEEP_OFF"));
	
	read(fd,data,strlen(data));
	printf("%s\n",data);
	close(fd);

	return 0;
}

(3)Makefile

#多个c文件生成一个ko文件。
obj-m +=gpio.o           #把.c文件的名字加.o写到这里

# KDIR 内核源码路径,根据自己需要设置
KDIR:=/home/qjl/work/lichee/linux-3.10

all:
#ARCH: 指当前编译的驱动模块的架构
#CROSS_COMPILE:指明交叉编译器的前缀
#-C: 指定去$(KDIR)目录下执行Makefile
#M:告知Makefile,需要的编译文件在哪
#modules: 这个规则是用于编译驱动模块的
	@make ARCH=arm64 CROSS_COMPILE=aarch64-linux- -C $(KDIR) M=$(PWD) modules 
	@rm -fr .tmp_versions *.o *.mod.o *.mod.c *.bak *.symvers *.markers *.unsigned *.order *~ .*.*.cmd .*.*.*.cmd
	
clean:
	@make ARCH=arm64 CROSS_COMPILE=aarch64-linux- -C $(KDIR) M=$(PWD) modules clean
	@rm -rf *.ko

   

五、实现写功能示例二(库函数申请一组gpio)

实现功能:绿灯亮5s—红灯亮5s—蜂鸣器响1s。
(1)gpio.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/sys_config.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>

#define LED_GREEN	    GPIOB(7)
#define BEEP            GPIOG(13)
#define LED_RED 	    GPIOB(6)


#define KBUF_SIZE (1024)

#define MIN(x, y) ((x)<(y)?(x):(y))
char kbuf[KBUF_SIZE];


int gpio_open (struct inode *node, struct file *fp)
{
	return 0;
}
int gpio_release (struct inode *node, struct file *fp)
{
	return 0;
}

ssize_t gpio_read (struct file *fp, char __user *ubuff, size_t size, loff_t *offset)
{
	return 0;
}


ssize_t gpio_write(struct file *fp, const char __user *ubuf, size_t size, loff_t *offset)
{
	int ret;
	memset(kbuf, 0, sizeof(kbuf));
	ret = copy_from_user(kbuf, ubuf, MIN(KBUF_SIZE, size));
	if(ret != 0) {
		pr_err("copy_from_user error\r\n");
		return -EINVAL;
	}
	//原理图中,beep高电平响,led低电平亮
	if( strstr(kbuf, "LED_GREEN_ON") )      gpio_set_value(LED_GREEN, 0);   
    else if(strstr(kbuf, "LED_GREEN_OFF"))  gpio_set_value(LED_GREEN, 1);
	
    else if(strstr(kbuf, "BEEP_ON"))        gpio_set_value(BEEP, 1);
    else if(strstr(kbuf, "BEEP_OFF"))       gpio_set_value(BEEP, 0);
	
    else if(strstr(kbuf, "LED_RED_ON"))     gpio_set_value(LED_RED, 0);
    else if(strstr(kbuf, "LED_RED_OFF"))    gpio_set_value(LED_RED, 1);
	
	return MIN(KBUF_SIZE, size);  //返回写入的字节数
}

const struct file_operations gpio_fops={
    .owner=THIS_MODULE,     //文件操作集的拥有者是 本模块。
    .read= gpio_read,
  	.write= gpio_write,
  	.open= gpio_open,
  	.release= gpio_release,
};

struct miscdevice mygpio={
	.minor=MISC_DYNAMIC_MINOR,    //系统自动分配次设备号
	.name ="my_gpio",
	.fops=&gpio_fops,
};

//定义一组gpio
const struct gpio gpio_array[] = {
	{LED_RED, 	GPIOF_OUT_INIT_HIGH, "LED_RED"},  //gpio端口、初始电平值、端口描述(任意)
	{LED_GREEN, GPIOF_OUT_INIT_HIGH, "LED_GREEN"},
	{BEEP, 		GPIOF_OUT_INIT_LOW,  "BEEP"},
};


static int __init gpio_init(void)
{
  int ret;
  ret=misc_register(&mygpio);
  if(ret!=0){
     pr_err("misc_register error");
	 return -EINVAL;   //返回错误编码
  }
  //先释放一次,不然申请会失败,因为有的gpio被其他的驱动申请过了
   gpio_free_array(gpio_array, sizeof(gpio_array)/sizeof(gpio_array[0]));
  //申请一组gpio
  ret = gpio_request_array(gpio_array,sizeof(gpio_array)/sizeof(gpio_array[0]));
  if(ret != 0) {
			pr_err("gpio_request_array error\r\n");
			goto error;   //如果失败跳到error去。
	}
    return 0;
  
error:
	//某一步出错后,要把他前面申请的资源释放掉,不然驱动再次安装时就会失败
	misc_deregister(&mygpio);
	return -1;
}

static void __exit gpio_exit(void)
{
 gpio_free_array(gpio_array, sizeof(gpio_array)/sizeof(gpio_array[0]));
 misc_deregister(&mygpio);  //从内核注销一个杂项设备
}

module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");

(2)app.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char **arg)
{
	int fd;
	fd = open("/dev/my_gpio", O_RDWR);
	if(fd < 0) {
		perror("open error");
		return -1;
	}

	write(fd, "LED_GREEN_ON", sizeof("LED_GREEN_ON"));
	sleep(5);
	write(fd, "LED_GREEN_OFF", sizeof("LED_GREEN_OFF"));
	
	write(fd, "LED_RED_ON", sizeof("LED_RED_ON"));
	sleep(5);
	write(fd, "LED_RED_OFF", sizeof("LED_RED_OFF"));
	
	write(fd, "	BEEP_ON", sizeof("	BEEP_ON"));
	sleep(1);
	write(fd, " BEEP_OFF", sizeof("	BEEP_OFF"));
	
	close(fd);
	return 0;
}

(3)Makefile文件与示例一相同。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值