[轉]android驅動例子(LED燈控制


本文轉自:http://blog.csdn.net/ok138ok/article/details/6317212

本例子,講述在android2.1上完全自已開發一個驅動去控制硬件口並寫應用測試該驅動,通過這樣一個例子,解析android下的驅動開發流程的應用調用流程,可以說是很好的入門引導

要達到的效果:通過android的應用,調用驅動程序,在開發板上控制4個LED的亮滅。

一、硬件原理

clip_image001

如上圖,通過4個IO口控制這LED,低電平LED亮,

這4個IO口分別是GPM1, GPM2, GPM3, GPM4,

二、驅動程序

1、在kernel文件夾下的driver目錄,新鍵驅動文件夾

# cd kernel_android_2.6.28.6/drivers

進到開發板的kernel目錄,建驅動文件夾

#mkdir ledtest

2、在/driver/ledtest目錄下,新建leddriver.c ,leddriver.h , Kconfig, Makefile 等4個文件

leddriver.c

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/init.h>            /* For __init/__exit/... */
#include <linux/module.h>
#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <plat/gpio-cfg.h>
#include <plat/regs-clock.h>
#include <plat/regs-lcd.h>
#include <plat/regs-gpio.h>
#include <mach/map.h>
#include <linux/gpio.h>
#include <plat/gpio-bank-m.h>
#include <linux/cdev.h>
#include <linux/fs.h> //for register_chrdev()
#include <linux/device.h>
#include <mach/map.h>
#include "leddriver.h"
#include <linux/miscdevice.h>        /* For MODULE_ALIAS_MISCDEV
 (WATCHDOG_MINOR) */
#include <linux/watchdog.h>        /* For the watchdog specific items */
#include <linux/fs.h>            /* For file operations */
#define Viberator_MAJOR        97  //主設備號
#define SCULL_NR_DEVS           4
#define SCULL_QUANTUM         4000
#define SCULL_QSET             1000
//---do as the GIO driver
#define DEVCOUNT                4
#define GIO_MINOR            2    /* GIO minor no. */
static dev_t dev;        //申 請 到的主 設備 號
static struct cdev *cdev_p;
static int openCnt;
//--全局變量------------
int VIB_major  = 97;//we asigment it for test
int VIB_minor = 0;
int VIB_nr_devs = SCULL_NR_DEVS;
int VIB_quantum = SCULL_QUANTUM;
int VIB_qset = SCULL_QSET;

static struct class *vib_dev_class;
#define GPNCON  S3C64XX_GPNCON 
#define GPNDAT  S3C64XX_GPNDAT
#define GPNPUD  S3C64XX_GPNPUD
#define GPMCON S3C64XX_GPMCON
#define GPMDAT  S3C64XX_GPMDAT
#define GPMPUD S3C64XX_GPMPUD

#define VIB_ON 0x11
#define  VIB_OFF 0x22
static const struct file_operations GPIO_Viberator_ctl_ops={
    .owner        = THIS_MODULE,
    .open    = GPIO_VIB_open,
    .read    =GPIO_VIB_read,
    .write    =GPIO_VIB_write,
    .ioctl     = GPIO_VIB_ioctl,
    .release     =GPIO_VIB_release,
};
ssize_t GPIO_VIB_read(struct file * file,char * buf,size_t count,loff_t * f_ops)
{
       char sdas[5]={1,3,8,1,6};
       ssize_t            status = 5;
    unsigned long    missing;
    missing = copy_to_user(buf, sdas, status);
    if (missing == status)
        status = -EFAULT;
    else
        status = status - missing;   
       printk(" GPIO_VIB_read = %d ,count = %d/r/n",status,count);
    gpio_direction_output(S3C64XX_GPM(3), 0);// 
return status ; 
}
ssize_t GPIO_VIB_write (struct file * file,const char * buf, size_t count,loff_t * f_ops)
{
    unsigned long missing;
     char inbuffer[512]={0};
    int i;
     missing = copy_from_user(inbuffer, buf, count);
    printk(" GPIO_VIB_write count = %d missing =%d /r/n",count,missing);
    gpio_direction_output(S3C64XX_GPM(3), 1);// 
    for(i=0;i<count;i++)
        {
            printk("Write:inbuffer[%d]=%d /r/n",i,inbuffer[i]);
        } 
return count;
}

//ssize_t GPIO_VIB_ioctl(struct inode * inode,struct file * file,unsigned int cmd, long data)
static int GPIO_VIB_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    printk(KERN_ERR"VIB:GPIO_VIB_ioctl --CMD=%x /n",cmd);
    switch(cmd)
        {
           case VIB_ON:
            gpio_direction_output(S3C64XX_GPM(1), 0);// 
            gpio_direction_output(S3C64XX_GPM(2), 0);// 
            gpio_direction_output(S3C64XX_GPM(3), 0);// 
            gpio_direction_output(S3C64XX_GPM(4), 1);// 
            printk(KERN_ERR"VIB:GPIO_VIB_ioctl --VIB_ON/n");
            break;
         case VIB_OFF:
            {
            printk(KERN_ERR"VIB:GPIO_VIB_ioctl --VIB_OFF/n");
            gpio_direction_output(S3C64XX_GPM(1), 1);// 
            gpio_direction_output(S3C64XX_GPM(2), 1);// 
            gpio_direction_output(S3C64XX_GPM(3), 1);// 
            gpio_direction_output(S3C64XX_GPM(4), 0);// 
            break;
         }
         default:break;
                
    }
        //gpio_free(S3C64XX_GPN(7));
}
ssize_t GPIO_VIB_open(struct inode * inode,struct file * file)
{
//模塊數
  //MOD_INC_USE_COUNT;
  printk("GPIO_VIB_open() /n");   
  return 0;
}
ssize_t GPIO_VIB_release(struct inode * inode, struct file * file)
{
   // MOD_DEC_USE_COUNT;//模塊數減1
     printk("GPIO_VIB_release() /n");   
    return 0;
}
static int GPIO_VIB_CTL_init(void)
{
    int ret  = -ENODEV;
   int error ;
    printk("---------------------------------------------- /r/n");
    //初始化端口
        s3c_gpio_cfgpin(S3C64XX_GPM(1), S3C_GPIO_SFN(1));//GPM1 output
        s3c_gpio_cfgpin(S3C64XX_GPM(2), S3C_GPIO_SFN(1));//GPM2 output
        s3c_gpio_cfgpin(S3C64XX_GPM(3), S3C_GPIO_SFN(1));//GPM3 output
        s3c_gpio_cfgpin(S3C64XX_GPM(4), S3C_GPIO_SFN(1));//GPM4 output
#if 1 /*靜態方式注冊驅動*/
ret = register_chrdev(Viberator_MAJOR, "viberator", &GPIO_Viberator_ctl_ops);
if (ret < 0) {
    printk(KERN_ERR "VIB: unable to get major %d/n", ret);
    return ret;
}
//創建class
vib_dev_class = class_create(THIS_MODULE, "viberator");
if (IS_ERR(vib_dev_class)) {
    unregister_chrdev(Viberator_MAJOR, "capi20");
    return PTR_ERR(vib_dev_class);
}
//創建節點,
device_create(vib_dev_class, NULL, MKDEV(Viberator_MAJOR, 0), NULL, "vib");
// create a point under /dev/class/vib 
//通過上面這兩步,驅動加載后,就會在/dev/class/下面生成vib節點,應用程序可以像操作文件那樣操作這個節點,通過open ,write,read 等函數操作,詳情可以看后面的應用示例程序。
  return 0;    
#endif
#if 0/* 注冊動態 */
    if ((error = alloc_chrdev_region(&dev, 0, DEVCOUNT, "vibrate")) < 0)
        {
        printk(KERN_ERR
               "VIB: Couldn't alloc_chrdev_region, error=%d/n",
               error);
        return 1;
    }
         printk("dev = %d /n",dev);
    cdev_p = cdev_alloc();
    cdev_p->ops = &GPIO_Viberator_ctl_ops;
    error = cdev_add(cdev_p, dev, DEVCOUNT);
    if (error) {
        printk(KERN_ERR
               "VIB: Couldn't cdev_add, error=%d/n", error);
        return 1;
    }

    vib_dev_class = class_create(THIS_MODULE, "vib-dev");
    if (IS_ERR(vib_dev_class)) {
        res = PTR_ERR(vib_dev_class);
        goto out_unreg_class;
    }
    return 0;
#endif
out_unreg_class:
    class_destroy(vib_dev_class);
return 1;
}

static int __init S3C6410_VIB_init(void)
{
    int ret = -ENODEV;
    //調用函數 
        printk(KERN_ERR "Auly: S3C6410_VIB_init---/n");
    ret = GPIO_VIB_CTL_init();
    if(ret)
        {
        printk(KERN_ERR "Auly: S3C6410_VIB_init--Fail !!!/n");
        return ret;
    }
    return 0;
}
static void __exit cleanup_GPIO_VIB(void)
{
    //注銷設備
  // devfs_unregister_chrdev(Viberator_MAJOR,"gpio_vib_ctl");
 #if 0 
      cdev_del(cdev_p);
    unregister_chrdev_region(dev, DEVCOUNT);
    class_destroy(vib_dev_class);
#endif 
device_destroy(vib_dev_class, MKDEV(Viberator_MAJOR, 0));
class_destroy(vib_dev_class);
unregister_chrdev(Viberator_MAJOR, "viberator");
}
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Peter first driver");
MODULE_ALIAS_CHARDEV(Viberator_MAJOR, 0);

module_init(S3C6410_VIB_init);
module_exit(cleanup_GPIO_VIB);

leddriver.h文件

leddriver.h文件
  1. ssize_t GPIO_VIB_read(struct file * file,char * buf,size_t count,loff_t * f_ops);
  2. ssize_t GPIO_VIB_write (struct file * file,constchar * buf, size_t count,loff_t * f_ops);
  3. static int GPIO_VIB_ioctl(struct inode *inode,struct file *file, unsigned int cmd, unsigned long arg);
  4. ssize_t GPIO_VIB_open(struct inode * inode,struct file * file);
  5. ssize_t GPIO_VIB_release(struct inode * inode,struct file * file);
  6. static int GPIO_VIB_CTL_init(void);

Kconfig 文件

Kconfig 文件
  1. config LEDTEST
  2.   tristate "LED test for ARMeasy"
  3.   default n
  4.   help
  5.   this is a LED driver for ARMEASY with S3C6410

Makefile文件

Makefile文件
  1. obj-$(CONFIG_LEDTEST)+=leddriver.o

3、在內核配置里菜單里加入本驅動的配置項

達到的效果是,可以通過內核配置來選擇是否把本驅動編譯進內核里,也就是出現在make menuconfig 后出來的界面里,可以參考9.8章<android添加新驅動方法>

1)在arch/arm/Kconfig文件里 menu "Device Drivers"與 endmenu之間添加

  1. source "drivers/ledtest/Kconfig"

2)在drivers/Kconfig menu "Device Drivers" 和 endmenu之間添加

  1. source "drivers/ledtest/Kconfig"

3)修改/drivers/Makefile文件

  1. Obj-$(CONFIG_LEDTEST) +=ledtest/

4、編譯驅動

在kernel目錄下,終端輸入

  1. #make menuconfig

“Device Drivers” 下面會看到 “LED test for ARMeasy”,選擇它,

保存並退出

  1. #make

這樣,就會在/drivers/ledtest目錄下得到leddrivr.ko文件,它就是驅動目標文件,已級編譯進了zImage 里了,只要用這個kernel燒錄到開發板,開機就會自動加載本驅動,

PS,如果發現如上編譯,沒有在ledtest目錄下生成leddriver.ko文件,也就是本根沒有去編譯本驅動,那就把ledtest目錄入到/drivers/misc目錄下,相應的修改misc下面,然后,在makefile里,不用選直接寫成obj-y += leddriver.o,這樣強制的包含進去編譯。

三、 應用程序(測試驅動)

用C語言寫一個應用,測試上面寫的驅動是否工作正常,這是一般的驅動開發流程

主要有兩個文件:Android.mk和ledtest.c

1、

在$(YOUR_ANDROID)/external/LEDTEST/目錄編寫ledtest.c文件,

ledtest.c

ledtest.c
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h>// contact the open(),close(),read(),write() and so on!
  4.   #define DEVICE_NAME"/dev/vib"//device point
  5. #define LED_ON     0x11
  6. #define LED_OFF     0x22
  7. int main(int argc,char **argv)
  8. {   
  9.     int fd;
  10.     int ret;
  11.     char *i;
  12.     printf("\n start gpio_led_driver test \r\n");
  13.     fd = open(DEVICE_NAME,O_RDWR);//Open device ,get the handle
  14.     printf("fd = %d \n",fd);
  15.     if(fd == -1) //open fail
  16.     {
  17.       printf("open device %s error \n",DEVICE_NAME);
  18.     }
  19.     else
  20.     {
  21.         while(1)
  22.         {
  23.           ioctl(fd,LED_OFF); //call the output function to off LEDs
  24.           sleep(1);//wait 1 second
  25.           ioctl(fd,LED_ON);
  26.           sleep(1);
  27.         }
  28.         ret = close(fd); //close device
  29.         printf("ret = %d \n",ret);
  30.         printf("close gpio_led test \n");
  31.     }
  32.   return 0;
  33. }

2、在$(YOUR_ANDROID)/external/ LEDTEST /目錄編寫Android.mk文件。這是Android Makefile的標准命名,不要更改。Android.mk文件的格式和內容可以參考其他已有的Android.mk文件的寫法,針對helloworld程序的Android.mk文件內容如下:

Android.mk
  1.  
  2. LOCAL_PATH:= $(call my-dir)
  3. include $(CLEAR_VARS)
  4. LOCAL_SRC_FILES:= \/par     hello.c
  5. LOCAL_MODULE := ledtestapp
  6. include $(BUILD_EXECUTABLE)

注意上面LOCAL_SRC_FILES用來指定源文件;

LOCAL_MODULE指定要編譯的模塊的名字,下一步驟編譯時就要用到;

include $(BUILD_EXECUTABLE)表示要編譯成一個可執行文件,如果想編譯成動態庫則可用BUILD_SHARED_LIBRARY,這些可以在

$(YOUR_ANDROID)/build/core/config.mk查到。

3、 回到Android源代碼頂層目錄進行編譯:

# cd $(YOUR_ANDROID) && make ledtestapp

例如我這里的實際操作是

#cd android2.1
#make ledtestapp

注意make ledtestapp中的目標名ledtestapp就是上面Android.mk文件中由LOCAL_MODULE指定的模塊名。編譯結果如下

make ledtestapp
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.1-update1
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=ECLAIR
============================================
/bin/bash: line 0: cd: sdk/layoutopt/app/src/resources: No such file or directory
target thumb C: ledtestapp <= external/LEDTEST/ledtest.c
Install: out/host/linux-x86/bin/apriori
Install: out/host/linux-x86/bin/soslim
target Executable: ledtestapp (out/target/product/generic/obj/EXECUTABLES/ledtestapp_intermediates/LINKED/ledtestapp)
target Non-prelinked: ledtestapp (out/target/product/generic/symbols/system/bin/ledtestapp)
target Strip: ledtestapp (out/target/product/generic/obj/EXECUTABLES/ledtestapp_intermediates/ledtestapp)
Install: out/target/product/generic/system/lib/libdl.so
Install: out/target/product/generic/system/lib/libc.so
Install: out/target/product/generic/system/lib/libstdc++.so
Install: out/target/product/generic/system/lib/libm.so
Install: out/target/product/generic/system/bin/ledtestapp

4、如上面的編譯結果所示,編譯后的可執行文件存放在

out/target/product/generic/system/bin/目錄 可執行文件是ledtestapp

5、拷貝ledtestapp可執行文件到T卡,插入開發板

6、開電,在啟動進入android的過程中,串口終端 按回車,進入命令行模式

7、拷貝可執行文件到根目錄

#cp /sdcard/ledtestapp /   

8、執行程序

#./ledtestapp

通過上面的步驟,可以看到開發板上相應的LED在閉,每秒鍾閃一次!


買開發板的話,推薦下面4款,你可以根據你的經濟能力來選一個
1、高級嵌入式開發板 ARM-A8內核,價格在2200~2700
X-210開發板(代碼開放度高,XBOOT很牛)   
Mini210開發板 (友善的板子,有沉淀)

2、入門級嵌入式開發板 ARM11內核  ,價格在500~650之間
Tiny6410    (友善之臂經典之作)     
OK6410    (特點是支持WINCE7.0)

可以去風火輪團隊的店里看一下,在他們那里買板有個好處,他們提從更周到的技術支持

風火輪技術開發團隊  http://www.arm-cn.com/

官方淘寶店:smartfire.taobao.com


csdn簽名

 

 

后語:近來老發現有網站盜用我的博客文章,又不注明出處,很不尊重我的勞動果實,所以設計了以上簽名,做成圖片,望理解!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM