openwrt之字符设备GPIO驱动

第五章 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的一系列操作

猜你喜欢

转载自blog.csdn.net/robothj/article/details/80036279