前言
写文章的目的是想通过记录自己的学习过程,以便以后使用到相关的知识点可以回顾和参考。
一、GPIO子系统提供的API
gpio 子系统提供了 API 函数来操作指定的 GPIO,gpio 子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处,大家各司其职,做好自己的本职工作即可。gpio 子系统提供的常用的 API 函数有下面几个:
1、gpio_request 函数
gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request 进行申请,函数原型如下:
int gpio_request(unsigned gpio, const char label)
函数参数和返回值含义如下:
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字。 返回值:0,申请成功;其他值,申请失败。
——————————————–——————————————————————
2、gpio_free 函数
如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。函数原型如下:
void gpio_free(unsigned gpio)
函数参数和返回值含义如下:
gpio:要释放的 gpio 标号。
返回值:无。
——————————————–——————————————————————
3、gpio_direction_input 函数
此函数用于设置某个 GPIO 为输入,函数原型如下所示:
int gpio_direction_input(unsigned gpio)
函数参数和返回值含义如下:
gpio:要设置为输入的 GPIO 标号。
返回值:0,设置成功;负值,设置失败。
——————————————–——————————————————————
4、gpio_direction_output 函数
此函数用于设置某个 GPIO 为输出,并且设置默认输出值,函数原型如下:
int gpio_direction_output(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置为输出的 GPIO 标号。
value:GPIO 默认输出值。
返回值:0,设置成功;负值,设置失败。
——————————————–——————————————————————
5、gpio_get_value 函数
此函数用于获取某个 GPIO 的值(0 或 1),此函数是个宏,定义所示:
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
函数参数和返回值含义如下:
gpio:要获取的 GPIO 标号。
返回值:非负值,得到的 GPIO 值;负值,获取失败。
——————————————–——————————————————————
6、gpio_set_value 函数
此函数用于设置某个 GPIO 的值,此函数是个宏,定义如下
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置的 GPIO 标号。
value:要设置的值。
返回值:无
关于 gpio 子系统常用的 API 函数就讲这些,这些是我们用的最多的。
二、驱动程序
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <cfg_type.h>
#define CHRLED_CNT 1 /* 设备号个数 */
#define CHRLED_NAME "led" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* 定义一个设备信息的结构体 */
struct dev_str{
dev_t devid; /* 设备号 */
struct cdev led_cdev; /* cdev */
struct class *led_class; /* 类 */
struct device *led_device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct dev_str led_dev;
/* 定义一个结构体存放gpio信息 */
struct led_gpio_info{
unsigned int gpio; /* GPIO标号 */
const char *name; /* 名字 */
};
static const struct led_gpio_info led_gpio_info_tbl[4]={
{
.gpio = PAD_GPIO_E+13, /* 标号的形式 */
.name = "gpioe13",
},
{
.gpio = PAD_GPIO_C+17,
.name = "gpioc17",
},
{
.gpio = PAD_GPIO_C+8,
.name = "gpioc8",
},
{
.gpio = PAD_GPIO_C+7,
.name = "gpioc7",
},
};
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &led_dev; /* 设置私有数据 */
//printk(KERN_EMERG "kernel open file ok!\r\n");
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[2];
unsigned char ledstat;
unsigned char lednum;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0)
{
printk(KERN_EMERG "kernel write failed!\r\n");
return -EFAULT;
}
lednum = databuf[0];
ledstat = databuf[1];
if(lednum >= 4){
printk(KERN_EMERG "kernel: lednum don't specification!\r\n");
return -EFAULT;
}
if(ledstat == LEDON){
gpio_set_value(led_gpio_info_tbl[lednum].gpio, 0);
}else if(ledstat == LEDOFF){
gpio_set_value(led_gpio_info_tbl[lednum].gpio, 1);
}
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
printk(KERN_EMERG "kernel close file ok!\r\n");
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
/* 驱动入口函数 */
static int __init led_init(void)
{
int retvalue;
u32 i = 0;
/* 初始化 LED */
/* 申请GPIO */
retvalue = gpio_request(led_gpio_info_tbl[i].gpio, led_gpio_info_tbl[i].name);
if(retvalue < 0){
printk(KERN_EMERG "kernel gpio_request failed!\r\n");
return -EIO;
}
/* 设置4个GPIO为输出模式, 默认关闭LED*/
for(i=0;i<4;i++){
gpio_direction_output(led_gpio_info_tbl[i].gpio, 1);
}
/* 申请设备号 */
if(led_dev.major){
/* 如果自定义了设备号 */
led_dev.devid = MKDEV(led_dev.major, 0);
retvalue = register_chrdev_region(led_dev.devid, CHRLED_CNT, CHRLED_NAME);
}else{
/* 否则,动态申请设备号 */
retvalue = alloc_chrdev_region(&led_dev.devid, 0, CHRLED_CNT, CHRLED_NAME);
led_dev.major = MAJOR(led_dev.devid);
led_dev.minor = MINOR(led_dev.devid);
}
printk(KERN_EMERG "led_major = %d\r\n",led_dev.major);
printk(KERN_EMERG "led_minor = %d\r\n",led_dev.minor);
if(retvalue < 0){
printk(KERN_EMERG "kernel register chrdev failed!\r\n");
return -EIO;
}
/* 字符设备初始化 */
led_dev.led_cdev.owner = THIS_MODULE;
cdev_init(&led_dev.led_cdev, &led_fops);
/* 把字符设备添加到内核 */
cdev_add(&led_dev.led_cdev, led_dev.devid, CHRLED_CNT);
/* 自动创建设备节点文件 */
/* 创建类 */
led_dev.led_class = class_create(THIS_MODULE, CHRLED_NAME);
/* 创建设备 */
led_dev.led_device = device_create(led_dev.led_class, NULL, led_dev.devid, NULL, CHRLED_NAME);
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
int i;
/* 释放GPIO */
for(i=0; i<4; i++){
gpio_free(led_gpio_info_tbl[i].gpio);
}
/* 删除设备 */
device_destroy(led_dev.led_class, led_dev.devid);
/* 删除类 */
class_destroy(led_dev.led_class);
/* 把字符设备从内核中删除 */
cdev_del(&led_dev.led_cdev);
/* 注销字符设备驱动 */
unregister_chrdev_region(led_dev.devid, CHRLED_CNT);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzj");
三、应用程序
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#define LEDOFF 0
#define LEDON 1
int main(int argc , char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char databuf[2];
if(argc != 4){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("Can't open file %s\r\n", filename);
return -1;
}
databuf[0] = atoi(argv[2]);
databuf[1] = atoi(argv[3]);
/* 向设备驱动写数据 */
retvalue = write(fd, databuf, sizeof(databuf));
if(retvalue < 0){
printf("LED Control failed!\r\n");
close(fd);
return -1;
}
/* 关闭设备 */
retvalue = close(fd);
if(retvalue < 0){
printf("Can't close file %s\r\n", filename);
return -1;
}
return 0;
}
四、测试
在开发板终端输入如下命令:
cd /lib/modules/3.4.39
insmod led.ko
./ledApp /dev/led 1 1
执行应用程序时需要输入4个参数,第3个参数为选择哪个led,第4个参数为点亮/关闭。