第五章 GPIO驱动
GPIO操作的几种方式
1. 在文件系统中使用寄存器REG工具直接操作
# reg
Usage : reg r addr
Usage : reg w addr value
# 读
# reg r 0x10000000
# 写
# reg w 0x10000064 0x552
# reg w 0x10000064 0x550
2. 在文件系统中通过内核提供的接口操作
Widora 默认支持9个 GPIO,分别是 GPIO0 (实为GPIO11),14,15,16,17,39,40,41,42。
# eg:导出gpio14
# cd /sys/class/gpio/
# echo 14 > export
# ls
# cd gpio14
# echo out > direction
# echo 1 > value
# echo 0 > value
# echo in > direction
# cat value
3. 编写内核驱动
编写GPIO驱动
1. 查找 GPIO 寄存器配置(gpio17为例)
- 查手册获取 GPIO17 设置相关的寄存器
GPIO#14~#17 脚是与 SPI-SLAVE 是功能复用的。需要通过设置 APGIO_CFG[20:17] (1111) 和 GPIO1_MODE[3:2] (01)来设置其为GPIO功能 - 通过设置 GPIO_CTRL_0 [17]为1将 GPIO17 设置为输出模式
- 通过设置 GPIO_DATA_0 [17]为0或1 将 GPIO17 输出设置为0或1
2. 建立文件目录
3. 编写leddrv.c (在模板的基础上修改即可)
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/crash_dump.h>
#include <linux/backing-dev.h>
#include <linux/bootmem.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/aio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/ioctl.h>
#define LED_ON 0
#define LED_OFF 1
//define registers
volatile unsigned long *GPIO_CTRL_0;// GPIO0-GPIO31 direction control register
volatile unsigned long *GPIO_DATA_0;// GPIO0-GPIO31 data register
volatile unsigned long *GPIO1_MODE;// GPIO1 purpose selection register
volatile unsigned long *AGPIO_CFG;// Analog GPIO configuartion, GPIO14-17 purpose
/********************** 基本定义 *******************************/
//内核空间缓冲区定义
#if 0
#define KB_MAX_SIZE 20
#define kbuf[KB_MAX_SIZE]
#endif
//加密函数参数内容: _IOW(IOW_CHAR , IOW_NUMn , IOW_TYPE)
//加密函数用于leddrv_ioctl函数中
//使用举例:ioctl(fd , _IOW('L',0x80,long) , 0x01)
//#define NUMn leddrv , if you need!
#define IOW_CHAR 'L'
#define IOW_TYPE long
#define IOW_NUM1 0x80
//初始化函数必要资源定义
//用于初始化函数当中
//device number;
dev_t dev_num;
//struct dev
struct cdev leddrv_cdev;
//auto "mknode /dev/leddrv c dev_num minor_num"
struct class *leddrv_class = NULL;
struct device *leddrv_device = NULL;
/**************** 结构体 file_operations 成员函数 *****************/
//open
static int leddrv_open(struct inode *inode, struct file *file)
{
printk("leddrv drive open, led pin set to 0\n");
*GPIO_DATA_0&=~(1<<17);
return 0;
}
//close
static int leddrv_close(struct inode *inode , struct file *file)
{
printk("leddrv drive close...\n");
return 0;
}
//read
static ssize_t leddrv_read(struct file *file, char __user *buffer, size_t len, loff_t *pos)
{
int ret_v = 0;
printk("leddrv drive read...\n");
return ret_v;
}
//write
static ssize_t leddrv_write( struct file *file , const char __user *buffer, size_t len , loff_t *offset )
{
int ret_v = 0;
printk("leddrv drive write...\n");
return ret_v;
}
//unlocked_ioctl
static int leddrv_ioctl (struct file *filp , unsigned int cmd , unsigned long arg)
{
int ret_v = 0;
printk("leddrv drive ioctl...\n");
switch(cmd)
{
case LED_ON:
*GPIO_DATA_0 |= (1<<17);
break;
case LED_OFF:
*GPIO_DATA_0 &= ~(1<<17);
break;
//带密码保护:
//请在"基本定义"进行必要的定义
case _IOW(IOW_CHAR,IOW_NUM1,IOW_TYPE):
{
if(arg == 0x1) //第二条件
{
}
}
break;
default:
break;
}
return ret_v;
}
/***************** 结构体: file_operations ************************/
//struct
static const struct file_operations leddrv_fops = {
.owner = THIS_MODULE,
.open = leddrv_open,
.release = leddrv_close,
.read = leddrv_read,
.write = leddrv_write,
.unlocked_ioctl = leddrv_ioctl,
};
/******************* functions: init , exit**********************/
//条件值变量,用于指示资源是否正常使用
unsigned char init_flag = 0;
unsigned char add_code_flag = 0;
//init
static __init int leddrv_init(void)
{
int ret_v = 0;
printk("leddrv drive init...\n");
//函数alloc_chrdev_region主要参数说明:
//参数2: 次设备号
//参数3: 创建多少个设备
if( ( ret_v = alloc_chrdev_region(&dev_num,0,1,"leddrv") ) < 0 )
{
goto dev_reg_error;
}
init_flag = 1; //标示设备创建成功
printk("The drive info of leddrv:\nmajor: %d\nminor: %d\n",
MAJOR(dev_num),MINOR(dev_num));
cdev_init(&leddrv_cdev,&leddrv_fops);
if( (ret_v = cdev_add(&leddrv_cdev,dev_num,1)) != 0 )
{
goto cdev_add_error;
}
leddrv_class = class_create(THIS_MODULE,"leddrv");
if( IS_ERR(leddrv_class) )
{
goto class_c_error;
}
leddrv_device = device_create(leddrv_class,NULL,dev_num,NULL,"leddrv");
if( IS_ERR(leddrv_device) )
{
goto device_c_error;
}
printk("auto mknod success!\n");
//------------ 请在此添加您的初始化程序 --------------//
AGPIO_CFG = (volatile unsigned long *)ioremap(0x1000003c,4); //init register address
GPIO1_MODE = (volatile unsigned long *)ioremap(0x10000060,4);
GPIO_CTRL_0 = (volatile unsigned long *)ioremap(0x10000600,4);
GPIO_DATA_0 = (volatile unsigned long *)ioremap(0x10000620,4);
*AGPIO_CFG|=(0x0f<<17);//set AGPIO_CFG[20:17] as 1111
*GPIO1_MODE&=~(1<<3); //set SPIS purpose as GPIO17
*GPIO1_MODE|=(1<<2);
*GPIO_CTRL_0|=(1<<17); //set GPIO17 as output pin
//如果需要做错误处理,请:goto leddrv_error
add_code_flag = 1;
//---------------------- END ---------------------------//
goto init_success;
dev_reg_error:
printk("alloc_chrdev_region failed\n");
return ret_v;
cdev_add_error:
printk("cdev_add failed\n");
unregister_chrdev_region(dev_num, 1);
init_flag = 0;
return ret_v;
class_c_error:
printk("class_create failed\n");
cdev_del(&leddrv_cdev);
unregister_chrdev_region(dev_num, 1);
init_flag = 0;
return PTR_ERR(leddrv_class);
device_c_error:
printk("device_create failed\n");
cdev_del(&leddrv_cdev);
unregister_chrdev_region(dev_num, 1);
class_destroy(leddrv_class);
init_flag = 0;
return PTR_ERR(leddrv_device);
//-------------------- 请在此添加您的错误处理内容 ------------------//
leddrv_error:
add_code_flag = 0;
return -1;
//--------------------- END --------------------//
init_success:
printk("leddrv init success!\n");
return 0;
}
//exit
static __exit void leddrv_exit(void)
{
printk("leddrv drive exit...\n");
if(add_code_flag == 1)
{
//-------------- 请在这里释放您的程序占有的资源 -----------//
printk("free your resources...\n");
iounmap(AGPIO_CFG); //release ioremap resource
iounmap(GPIO1_MODE);
iounmap(GPIO_CTRL_0);
iounmap(GPIO_DATA_0);
printk("free finish\n");
//----------------------- END --------------------//
}
if(init_flag == 1)
{
//释放初始化使用到的资源
cdev_del(&leddrv_cdev);
unregister_chrdev_region(dev_num, 1);
device_unregister(leddrv_device);
class_destroy(leddrv_class);
}
}
/**************** module operations************************/
//module loading
module_init(leddrv_init);
module_exit(leddrv_exit);
//some infomation
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("from Jafy");
MODULE_DESCRIPTION("leddrv drive");
/********************* The End ***************************/
Makefile
src 目录下
obj-m += leddrv.o
上级目录下
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk
PKG_NAME:=leddrv
PKG_RELEASE:=1
define KernelPackage/leddrv
SUBMENU:=Other modules
TITLE:=leddrv
FILES:=$(PKG_BUILD_DIR)/leddrv.ko
KCONFIG:=
endef
define KernelPackage/leddrv/description
This is a led drivers
endef
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
SUBDIRS="$(PKG_BUILD_DIR)"
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) modules
endef
$(eval $(call KernelPackage,leddrv))
编写应用程序
建立文件目录
led_app.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#define LED_ON 0
#define LED_OFF 1
char str_dev[]="/dev/leddrv";
int main(int argc,char* argv[])
{
int fd;
if(argc!=2)
{
printf(" %s <on|off> to turn on or off LED on GPIO17\n",argv[0]);
return -1;
}
fd=open(str_dev,O_RDWR|O_NONBLOCK); //--- GPIO17 will set to 0 when open
if(fd<0)
{
printf("can't open %s \n",str_dev);
return -1;
}
if(!strcmp("on",argv[1]))
{
while(1)
{
ioctl(fd,LED_ON);
usleep(50000);
ioctl(fd,LED_OFF);
usleep(50000);
}
}
else if(!strcmp("off",argv[1]))
ioctl(fd,LED_OFF);
else
{
printf(" %s <on|off> to turn on or off LED on GPIO17\n",argv[0]);
return -1;
}
return 0;
}
Makefile
src 目录下
all: led_app
OBJS = led_app.o
CC = gcc
CCFLAGS = -Wall -c -o
%.o: %.c
$(CC) $(CCFLAGS) $@ $< $(LDFLAGS)
fbtest: $(OBJS)
$(CC) -o $@ $(OBJS) $(LDFLAGS)
clean:
rm -f rbcfg *.o
上级目录下
##Copyright (C) 2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=led_app
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/led_app
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Frame buffer device testing tool
DEPENDS:=+libncurses
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Configure
endef
TARGET_LDFLAGS :=
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) \
CC="$(TARGET_CC)" \
CFLAGS="$(TARGET_CFLAGS) -Wall" \
LDFLAGS="$(TARGET_LDFLAGS)"
endef
define Package/led_app/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/led_app $(1)/usr/sbin/
endef
$(eval $(call BuildPackage,led_app))
备注:可以通过命令行嵌入到c程序中实现gpio的一系列操作