五、Linux内核驱动gpio库函数编写

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)申请gpioint gpio_request(unsigned gpio, const char *label);
    返回值: 成功:0    失败:<0
   
(4)释放gpiovoid 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)申请一个gpioint gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
    返回值: 成功:    失败:<0
   
(8) 申请一组gpioint gpio_request_array(const struct gpio *array, size_t num);
    返回值: 成功:     失败:<0
   
(9) 释放一组gpiovoid gpio_free_array(const struct gpio *array, size_t num);
   
(10)gpio 编号转换外部中断编号int gpio_to_irq(unsigned gpio);
    返回值: 成功:>=0     失败:<0
   
(11)外部中断编号转为gpioint 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)

功能:绿灯亮10s—打印“ hello!”(根据开发板原理图:绿灯io口为:GPIOB 7)
(1)gpio.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LED_GREEN	    GPIOB(7)
#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, "LED_GREEN_ON") )      gpio_set_value(LED_GREEN, 0);   
    else if(strstr(kbuf, "LED_GREEN_OFF"))  gpio_set_value(LED_GREEN, 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(LED_GREEN, "LED_GREEN");
  if(ret != 0) {
		pr_err("gpio_request error\r\n");
		return -1;
	}  
 //配置gpio的方向、无效电平
  gpio_direction_output(LED_GREEN, 1);   
    return 0;
}

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

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

(2)app.c

#include 
#include 
#include 
#include 
#include 
#include 

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, "LED_GREEN_ON", sizeof("LED_GREEN_ON"));
	sleep(10);
	write(fd, "LED_GREEN_OFF", sizeof("LED_GREEN_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 
#include 
#include 
#include 
#include 
#include 
#include 

#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 
#include 
#include 
#include 
#include 
#include 
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文件与示例一相同。

你可能感兴趣的:(linux,单片机,运维)