android驱动例子(LED灯控制)
本例子,讲述在android2.1上完全自已开发一个驱动去控制硬件口并写应用测试该驱动,通过这样一个例子,解析android下的驱动开发流程的应用调用流程,可以说是很好的入门引导
要达到的效果:通过android的应用,调用驱动程序,在开发板上控制4个LED的亮灭。
一、硬件原理
如上图,通过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文件
- ssize_t GPIO_VIB_read(struct file * file,char * buf,size_t count,loff_t * f_ops);
- ssize_t GPIO_VIB_write (struct file * file,constchar * buf, size_t count,loff_t * f_ops);
- static int GPIO_VIB_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg);
- ssize_t GPIO_VIB_open(struct inode * inode,struct file * file);
- ssize_t GPIO_VIB_release(struct inode * inode,struct file * file);
- static int GPIO_VIB_CTL_init(void);
Kconfig 文件
- config LEDTEST
- tristate "LED test for ARMeasy"
- default n
- help
- this is a LED driver for ARMEASY with S3C6410
- obj-$(CONFIG_LEDTEST)+=leddriver.o
3、在内核配置里菜单里加入本驱动的配置项
达到的效果是,可以通过内核配置来选择是否把本驱动编译进内核里,也就是出现在make menuconfig 后出来的界面里,可以参考9.8章<android添加新驱动方法>
1)在arch/arm/Kconfig文件里 menu "Device Drivers"与 endmenu之间添加
- source "drivers/ledtest/Kconfig"
2)在drivers/Kconfig menu "Device Drivers" 和 endmenu之间添加
- source "drivers/ledtest/Kconfig"
3)修改/drivers/Makefile文件
- Obj-$(CONFIG_LEDTEST) +=ledtest/
4、编译驱动
在kernel目录下,终端输入
- #make menuconfig
“Device Drivers” 下面会看到 “LED test for ARMeasy”,选择它,
保存并退出
- #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
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>// contact the open(),close(),read(),write() and so on!
- #define DEVICE_NAME"/dev/vib"//device point
- #define LED_ON 0x11
- #define LED_OFF 0x22
- int main(int argc,char **argv)
- {
- int fd;
- int ret;
- char *i;
- printf("\n start gpio_led_driver test \r\n");
- fd = open(DEVICE_NAME,O_RDWR);//Open device ,get the handle
- printf("fd = %d \n",fd);
- if(fd == -1) //open fail
- {
- printf("open device %s error \n",DEVICE_NAME);
- }
- else
- {
- while(1)
- {
- ioctl(fd,LED_OFF); //call the output function to off LEDs
- sleep(1);//wait 1 second
- ioctl(fd,LED_ON);
- sleep(1);
- }
- ret = close(fd); //close device
- printf("ret = %d \n",ret);
- printf("close gpio_led test \n");
- }
- return 0;
- }
2、在$(YOUR_ANDROID)/external/ LEDTEST /目录编写Android.mk文件。这是Android Makefile的标准命名,不要更改。Android.mk文件的格式和内容可以参考其他已有的Android.mk文件的写法,针对helloworld程序的Android.mk文件内容如下:
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= \/par hello.c
- LOCAL_MODULE := ledtestapp
- 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: <span class="kwrd">out</span>/host/linux-x86/bin/apriori
Install: out/host/linux-x86/bin/soslim
target Executable: ledtestapp (<span class="kwrd">out</span>/target/product/generic/obj/EXECUTABLES/ledtestapp_intermediates/LINKED/ledtestapp)
target Non-prelinked: ledtestapp (out/target/product/generic/symbols/system/bin/ledtestapp)
target Strip: ledtestapp (<span class="kwrd">out</span>/target/product/generic/obj/EXECUTABLES/ledtestapp_intermediates/ledtestapp)
Install: out/target/product/generic/system/lib/libdl.so
Install: <span class="kwrd">out</span>/target/product/generic/system/lib/libc.so
Install: out/target/product/generic/system/lib/libstdc++.so
Install: <span class="kwrd">out</span>/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在闭,每秒钟闪一次!
android驱动例子(LED灯控制)的更多相关文章
- 基于TINY4412的Andorid开发-------简单的LED灯控制【转】
本文转载自:http://www.cnblogs.com/pengdonglin137/p/3857724.html 基于TINY4412的Andorid开发-------简单的LED灯控制 阅读 ...
- Tiny4412之蜂鸣器驱动与led灯驱动
一:LED驱动编写 要编写LED驱动,首先的知道开发板的构造:开发板分为核心板与底板:编写驱动的第一步就是要看开发板,找到LED灯在开发板上的位置及所对应的名字:第一步就要查看核心板电路图,以及底板电 ...
- [转] 基于TINY4412的Andorid开发-------简单的LED灯控制
阅读目录 一.编写驱动程序 二.编写代码测试驱动程序 三.编写HAL代码 四.编写Framework代码 五.编写JNI代码 六.编写App 参考资料: <Andriod系统源代码情景分析> ...
- 基于TINY4412的Andorid开发-------简单的LED灯控制
参考资料: <Andriod系统源代码情景分析> <嵌入式Linux系统开发完全手册_基于4412_上册> 作者:彭东林 邮箱:pengdonglin137@163.com 平 ...
- 【转载】基于TINY4412的Andorid开发-------简单的LED灯控制
阅读目录(Content) 一.编写驱动程序 二.编写代码测试驱动程序 三.编写HAL代码 四.编写Framework代码 五.编写JNI代码 六.编写App 参考资料: <Andriod系统源 ...
- CC2530 LED灯控制
有三个寄存器 分别是功能寄存器.方向寄存器.配置寄存器 功能寄存器 通用 I/O口 说的就是一般的I/O口,可以输出高低电平 为外设IO 例如摄像头或者打印机他们具体实现某些功能(拍照或打印文档) ...
- Android驱动入门-Led控制+app+ndk库+底层驱动
硬件平台: FriendlyARM Tiny4412 Cortex-A9 操作系统: UBUNTU 14.04 LTS 时间:2016-09-20 21:56:48 本次实验使用的是 安卓APP + ...
- android驱动学习---led实验
======================== 驱动: 内核:android-kernel 2.6.36 (必须对应你的板子上内核,不然会出现insmod错误) 目的:通过android应用层用户 ...
- 让USB键盘的LED灯听你的!(不干扰使用)
最近在研究一个课题,如何能利用键盘的led灯通道进行有效通信,那么首先要做的就是尝试能否在不影响键盘的情况下控制LED灯(num lock ,caps lock ,scroll lock)的使用. 首 ...
随机推荐
- android addCategory()等说明
一.隐式意图介绍 显式意图我们前面已经提到,形如: Intent intent = new Intent(); intent.setClass(this,Other.class);//此句表示显式意图 ...
- Oracle导出表
方法一:利用PL/SQL Developer工具导出: 菜单栏-->Tools-->Export Tables,如下图,设置相关参数即可: 方法二:可以用cmd的操作命令导出,详情请去百度 ...
- linux下内存的统计和内存泄露类问题的定位
在产品的开发中,通过对当前系统消耗内存总量的统计,可以对产品所需内存总量进行精确的评估,从而选择合适的内存芯片与大小,降低产品的成本.在遇到内存泄露类问题时,经常会对此束手无策,本文通过对proc下进 ...
- mybatis insert 返回主键
分享牛,分享牛原创.ssm整合的时候,我们操作mybatis insert 的时候,需要返回插入的主键,因为主键是自增的,这个时候怎么办呢?很简单看一下下面的代码示例: 1.1.1. 代码定义 pub ...
- Dynamics CRM2013 Odata的filter中含有日期字段时遇到的一个奇葩问题
在使用Odata拼写filter时我们一般都用工具,因为手写是件极不靠谱且错误率极高的事,下图是我用query designer拼出来的一个filter,因为时间是参数,所以在拷贝出下面这段filte ...
- BI Publisher(rtf)模板开发语法大全
Rtf模板开发例如背景,纹理分栏等等功能都能用word工具实现不再具体总结大家可以参考word教程..... 一.组 定义一个组的目的是告诉XMLPublisher对重复的数据行进行循环显示,也就 ...
- iOS网络基础
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51376048 本文出自:[openXu的博客] 常用类 get请求 post请求 NSURL ...
- React Native组件只Image
不管在Android还是在ios原生的开发中,图片都是作为控件给出来的,在RN中也有这么一个控件(Image).根据官网的资料,图片分为本地静态图片,网络图片和混合app资源.一下分类介绍来源官网. ...
- 【ShaderToy】边栏的小雨伞
写在前面 我在9月份的时候对博客的主页换了个模板,一些童鞋可能会发现边栏多了个小雨伞的动画,再细心的同学可能会发现如果一直开着我的博客电脑耗电更快了--当然啦,也有可能你看到的是一团黑,这说明你该换更 ...
- Mybatis源码分析之存储过程调用
这一篇博客我们学习一下Mybatis调用存储过程的使用和运行流程.首先我们先创建一个简单的存储过程 DELIMITER $ CREATE PROCEDURE mybatis.ges_user_coun ...