基于TINY4412的Andorid开发-------简单的LED灯控制

2019-07-12 22:47发布

参考资料: 《Andriod系统源代码情景分析》 《嵌入式Linux系统开发完全手册_基于4412_上册》 作者:彭东林 邮箱:pengdonglin137@163.com 平台介绍: 主机:Win7 32位 虚拟机:VMware10 + ubuntu-12.04.2-desktop-amd64 Android版本:  android-4.2.2_r1 Linux内核版本:linux-3.5.0 Bootloader:  友善之臂提供的Superboot4412.bin 目标平台:tiny4412ADK+S700 4GB Flash   目的: 在Tiny4412上运行的Android系统上,通过点击屏幕上的Button来控制Tiny4412的核心板上的四个LED灯的亮灭。一个有八个Button,每个灯的亮灭通过两个灯来控制,点击ON,相应的LED亮;点击OFF,相应的LED灯灭。   下面分几步完成: 1、编写驱动程序 2、测试驱动程序 3、编写HAL代码 4、编写framework代码 5、编写JNI代码 6、编写App 下面开始:

一、编写驱动程序

分析tiny4412的原理图,看一下LED灯的位置: image 可以知道,LED是低电平亮,高电平灭。 看一下,接到了Exynos4412的哪些引脚上了: image 可以看到: LED1  --------- GPM4_0 LED2  --------- GPM4_1 LED3  --------- GPM4_2 LED4  --------- GPM4_3 看一下Exynos4412的芯片手册,看一下GPM4的相关寄存器: image 图中第二列表示的相对于基地址的偏移量,这里基地址是:0x11000000. 在芯片手册的Page288 ~ Page291对这些寄存器有更详细的介绍。 以GPM4_0引脚为例: image 为了控制灯,[3:0]应设置为0x01,即输出模式 image 向GPM4DAT的第0位写0,GPM4_0引脚输出低电平,LED1亮; 向GPM4DAT的第0位写1,GPM4_0引脚输出高电平,LED1灭; 接下来,开始写驱动程序,用友善之臂自带的Linux3.5.0内核 1: cd linux-3.5/ 2: cd drivers/ 3: mkdir android_led 4: cd android_led/ 在android_led/下创建led_demo.c和led_demo.h文件: touch led_demo.c led_demo.h 再在其中创建Makefile和Kconfig文件 touch Makefile Kconfig
  • 修改Kconfig:
1: config LED_DEMO 2: tristate "Android Led Demo" 3: default n 4: help 5: This is the led demo for Android system.
  • 修改Makefile:
obj-$(CONFIG_LED_DEMO) += led_demo.o
  • 修改drivers/Kconfig,添加 source “drivers/android_led/Kconfig”
1: menu "Device Drivers" 2:   3: source "drivers/android_led/Kconfig" 4:   5: ...... 6:   7: endmenu
  • 修改drivers/Makefile:
1: ...... 2:   3: obj-$(CONFIG_LED_DEMO) += android_led/
  • 在内核顶层目录下执行make  menuconfig,进入Device Drivers,将 Android Led Demo选择为*,然后保存配置退出。
image   注:执行上面这些操作之前,确保已经按照友善之臂的手册,成功编译了Android上用的Linux内核,并且在arch/arm/boot下生成了zImage等文件。 在前期开发的时候,时不时要编译,可以将drivers/android_led/Makefile修改为: 1: #obj-$(CONFIG_LED_DEMO) += led_demo.o 2: obj-m += led_demo.o 编译的时候,可以使用: make M=drivers/android_led modules 目的是提高编译速度,最后再将Makfile改回原样。临时测试,可以在Wind7的命令行下,使用adb push将led_demo.ko上传到/data/local下,然后用adb shell登陆板子,进行测试。
  • 修改led_demo.h和led_demo.c
led_demo.h 1: #ifndef __LED_DEMO_H__ 2: #define __LED_DEMO_H__ 3:   4: #include 5:   6: #define LED_ON _IOW('L', 0, int) 7: #define LED_OFF _IOW('L', 1, int) 8:   9: #define LED_DEMO_DEVICE_NODE_NAME "led_demo" 10: #define LED_DEMO_DEVICE_CLASS_NAME "led_demo" 11: #define LED_DEMO_DEVICE_FILE_NAME "led_demo" 12:   13: #define EXYNOS4412_GPM4CON 0x110002E0 14: #define EXYNOS4412_GPM4DAT 0x110002E4 15:   16:   17: struct led_demo_dev 18: { 19: struct cdev dev; 20: }; 21:   22: #endif   led_demo.c 1: #include 2: #include 3: #include 4: #include 5: #include 6:   7: #include 8: #include 9:   10:   11: #include "led_demo.h" 12:   13:   14: MODULE_LICENSE("GPL"); 15:   16:   17: static int led_demo_major; 18: static int led_demo_minor; 19: static int number_of_dev = 1; 20:   21: static struct led_demo_dev *led_dev = NULL; 22:   23: static unsigned int *GPM4CON = NULL; 24: static unsigned int *GPM4DAT = NULL; 25:   26: static struct class *led_demo_class = NULL; 27:   28:   29: static int led_open (struct inode *node, struct file *fops) 30: { 31: struct led_demo_dev *dev; 32:   33: dev = container_of(node->i_cdev, struct led_demo_dev, dev); 34:   35: fops->private_data = dev; 36:   37: return 0; 38: } 39: static int led_close (struct inode *node, struct file *fops) 40: { 41: return 0; 42: } 43:   44: static long led_ioctl (struct file *fops, unsigned int cmd, unsigned long data) 45: { 46: //struct led_demo_dev * led_dev = (struct led_demo_dev *)fops->private_data; 47:   48: if((data < 1) || (data > 4)) 49: { 50: printk(KERN_ALERT"parameter is no valid./n"); 51: return -EINVAL; 52: } 53: 54: switch (cmd) 55: { 56: case LED_OFF: 57: writel(readl(GPM4DAT) | (0x1<<(data-1)), GPM4DAT); 58: break; 59: case LED_ON: 60: writel(readl(GPM4DAT) & ~(0x1<<(data-1)), GPM4DAT); 61: break; 62: default: 63: return -EINVAL; 64: break; 65: } 66:   67: 68: return 0; 69: } 70:   71: struct file_operations led_fops = 72: { 73: .owner = THIS_MODULE, 74: .open = led_open, 75: .unlocked_ioctl = led_ioctl, 76: .compat_ioctl = led_ioctl, 77: .release = led_close, 78: }; 79:   80: static int __led_setup_dev(struct led_demo_dev * dev) 81: { 82: int err = -1; 83:   84: dev_t devno = MKDEV(led_demo_major, led_demo_minor); 85:   86: memset(dev, 0, sizeof(struct led_demo_dev)); 87:   88: cdev_init(&(dev->dev), &led_fops); 89:   90: dev->dev.owner = THIS_MODULE; 91:   92: err = cdev_add(&(dev->dev), devno, number_of_dev); 93: if(err < 0) 94: { 95: return err; 96: } 97: 98: return 0; 99: } 100:   101: static int led_demo_init(void) 102: { 103: int err = -1; 104: dev_t dev; 105: struct device *temp = NULL; 106:   107: printk(KERN_ALERT"Initializing led demo device./n"); 108:   109: err = alloc_chrdev_region(&dev, 0, number_of_dev, LED_DEMO_DEVICE_NODE_NAME); 110: if(err < 0) 111: { 112: printk(KERN_ALERT"fail to alloc char dev region./n"); 113: goto fail; 114: } 115:   116: led_demo_major = MAJOR(dev); 117: led_demo_minor = MINOR(dev); 118:   119: led_dev = kmalloc(sizeof(struct led_demo_dev), GFP_KERNEL); 120: if(!led_dev) 121: { 122: err = -ENOMEM; 123: printk(KERN_ALERT"Failed to alloc led device./n"); 124: goto unregister; 125: } 126:   127: err = __led_setup_dev(led_dev); 128: if (err < 0) 129: { 130: printk(KERN_ALERT"Failed to setup led device./n"); 131: goto clean_up; 132: } 133:   134: GPM4CON = (unsigned int *)ioremap(EXYNOS4412_GPM4CON, 4); 135: if(!GPM4CON) 136: { 137: err = -ENOMEM; 138: goto destroy_cdev; 139: } 140: 141: GPM4DAT = (unsigned int *)ioremap(EXYNOS4412_GPM4DAT, 4); 142: if(!GPM4DAT) 143: { 144: err = -ENOMEM; 145: goto unmap1; 146: } 147:   148: writel((readl(GPM4CON) & ~0xffff) | 0x1111, GPM4CON); 149: writel(readl(GPM4DAT)| 0xf, GPM4DAT); 150:   151: led_demo_class = class_create(THIS_MODULE, LED_DEMO_DEVICE_CLASS_NAME); 152: if(IS_ERR(led_demo_class)) 153: { 154: err = PTR_ERR(led_demo_class); 155: printk(KERN_ALERT"Failed to create led demo class./n"); 156: goto unmap2; 157: } 158:   159: temp = device_create(led_demo_class, NULL, dev, NULL, "%s", LED_DEMO_DEVICE_FILE_NAME); 160: if(IS_ERR(temp)) 161: { 162: err = PTR_ERR(temp); 163: printk(KERN_ALERT"Failed to create led demo device./n"); 164: goto destroy_class; 165: } 166:   167: dev_set_drvdata(temp, (void *)led_dev); 168:   169: printk(KERN_ALERT"Succeed to initialize led demo device./n"); 170: 171: return 0; 172:   173: destroy_class: 174: class_destroy(led_demo_class); 175: 176: unmap2: 177: iounmap(GPM4DAT); 178: 179: unmap1: 180: iounmap(GPM4CON); 181: 182: destroy_cdev: 183: cdev_del(&(led_dev->dev)); 184:   185: clean_up: 186: kfree(led_dev); 187: 188: unregister: 189: unregister_chrdev_region(MKDEV(led_demo_major, led_demo_minor), number_of_dev); 190: 191: fail: 192:   193: return err; 194: } 195:   196: static void led_demo_exit(void) 197: { 198: if(led_demo_class) 199: { 200: device_destroy(led_demo_class, MKDEV(led_demo_major, led_demo_minor)); 201: class_destroy(led_demo_class); 202: } 203: 204: iounmap(GPM4DAT); 205: iounmap(GPM4CON); 206:   207: if(led_dev) 208: { 209: cdev_del(&(led_dev->dev)); 210: kfree(led_dev); 211: } 212:   213: unregister_chrdev_region(MKDEV(led_demo_major, led_demo_minor), number_of_dev); 214: } 215:   216:   217:   218: module_init(led_demo_init); 219: module_exit(led_demo_exit); 220:     编写完成后,在内核源码的顶层目录执行make zImage –jN,然后就会在arch/arm/boot/生成zImage文件,利用友善之臂提供的Minitools将zImage烧写到板子上。具体步骤,参考友善之臂提供的PDF文档:《Tiny4412用户手册》

二、编写代码测试驱动程序

android-4.2.2_r1源码顶层目录下 1: external/led_demo/ 2: ├── Android.mk 3: ├── led_demo.c 4: └── led_demo.h 即,在external/下创建led_demo目录,并在其中创建Android.mk、led_demo.c以及led_demo.h文件. Android.mk: 1: LOCAL_PATH:= $(call my-dir) 2: include $(CLEAR_VARS) 3: LOCAL_MODULE_TAGS := optional 4: LOCAL_SRC_FILES := $(call all-subdir-c-files) 5: LOCAL_MODULE := led_demo_test 6: include $(BUILD_EXECUTABLE) 7:   led_demo.h: 1: #ifndef __LED_DEMO_H__ 2: #define __LED_DEMO_H__ 3:   4: #define LED_ON _IOW('L', 0, int) 5: #define LED_OFF _IOW('L', 1, int) 6:   7: #endif led_demo.c: 1: #include 2: #include 3: #include 4: #include 5: #include 6: #include 7:   8: #include "led_demo.h" 9:   10: int main(int argc, const char *argv[]) 11: { 12: int fd; 13: int i; 14:   15: fd = open("/dev/led_demo", O_RDWR); 16: if (fd < 0) 17: { 18: perror("failed to open./n"); 19: exit(-1); 20: } 21:   22: while(1) 23: { 24: for(i=0; i<4; i++) 25: { 26: ioctl(fd, LED_OFF, i+1); 27: sleep(1); 28: ioctl(fd, LED_ON, i+1); 29: sleep(1); 30: ioctl(fd, LED_OFF, i+1); 31: sleep(1); 32: } 33: } 34:   35: close(fd); 36:   37: return 0; 38: } 编写完成后,在android-4.2.2_r1源码顶层目录下执行: 1: mmm ./external/led_demo/ 2:   3: ./gen-img.sh 然后将顶层目录下新生成的system.img利用友善之臂提供的Minitools烧写到板子上。 烧写完成后,重启板子。 使用串口终端登陆板子,使用su命令进入root用户模式,然后进入/system/bin目录下,执行./led_demo_test,观察现象,可以看到,TINY4412的核心板上的四个LED灯循环亮灭。也可以使用wind7下的控制终端,用adb shell登陆板子,进行测试。

三、编写HAL代码

在hardware/libhardware/include/hardware/下创建文件led_demo_hal.h 在hardware/libhardware/modules/下创建目录led_demo_hal,然后进入led_demo_hal,创建两个文件,分别是Android.mk和 led_demo_hal.cpp。 下面是文件内容: hardware/libhardware/include/hardware/led_demo_hal.h 1: #ifndef ANDROID_LED_DEMO_HAL_H 2: #define ANDROID_LED_DEMO_HAL_H 3:   4: #include 5:   6: __BEGIN_DECLS 7:   8: #define LED_DEMO_HARDWARE_MODULE_ID "led_demo_hal" //模块ID 需要与下面的Android.mk中的LOCAL_MODULE 匹配,否则无法加载该HAL模块 9: #define LED_DEMO_HARDWARE_DEVICE_ID "led_demo" // 设备ID 10:   11:   12: struct led_demo_module_t 13: { 14: struct hw_module_t common; 15: }; 16:   17: struct led_demo_device_t 18: { 19: struct hw_device_t common; 20: int fd; 21: int (*set_on)(struct led_demo_device_t *dev, int val); //用于控制LED,点亮第val个LED灯 22: int (*set_off)(struct led_demo_device_t *dev, int val); //熄灭第val个LED灯 23: }; 24:   25: __END_DECLS 26:   27:   28: #endif   hardware/libhardware/modules/led_demo_hal/led_demo_hal.cpp 1: #define LOG_TAG "LED_DEMO_HALSTUB" //将来可以用DDMS的LogCat工具进行调试,便于查看打印信息 2:   3: #include 4: #include 5:   6: #include 7: #include 8:   9: #include 10: #include 11:   12:   13: #define DEVICE_NAME "/dev/led_demo" //设备结点,有Linux驱动程序自动创建 14: #define MODULE_NAME "led_demo" 15: #define MODULE_AUTHOR "pengdonglin137@163.com" 16:   17: #define LED_ON 0x40044c00 //点灯的命令,其实就是_IOW('L', 0, int)的值,_IOW在编译时无法识别,待以后解决 18: #define LED_OFF 0x40044c01 //灭灯命令,其实就是_IOW('L', 1, int)的值,可以在上面的led_demo.c中加打印,看一下这个值是多少 19:   20:   21: static int led_demo_open(const struct hw_module_t* module, const char* id, 22: struct hw_device_t** device); 23:   24: static int led_demo_close(struct hw_device_t* device); 25:   26: static int led_demo_set_on(struct led_demo_device_t *dev, int val); 27:   28: static int led_demo_set_off(struct led_demo_device_t *dev, int val); 29:   30:   31: static hw_module_methods_t led_demo_module_methods = 32: { 33: open:led_demo_open, 34: }; 35:   36: struct led_demo_module_t HAL_MODULE_INFO_SYM = 37: { 38: common:{ 39: tag:HARDWARE_MODULE_TAG, 40: version_major:1, 41: version_minor:0, 42: id:LED_DEMO_HARDWARE_MODULE_ID, 43: name:MODULE_NAME, 44: author:MODULE_AUTHOR, 45: methods:&led_demo_module_methods, 46: } 47: }; 48:   49: static int led_demo_open(const struct hw_module_t* module, const char* id, 50: struct hw_device_t** device) 51: { 52: if(!strcmp(id, LED_DEMO_HARDWARE_DEVICE_ID)) 53: { 54: struct led_demo_device_t *dev; 55:   56: dev = (struct led_demo_device_t *)malloc(sizeof(struct led_demo_device_t)); 57: if(!dev) 58: { 59: ALOGE("Failed to alloc space for struct led_demo_device_t."); 60: return -EFAULT; 61: } 62:   63: memset(dev, 0, sizeof(struct led_demo_device_t)); 64:   65: dev->common.tag = HARDWARE_DEVICE_TAG ; 66: dev->common.version = 0; 67: dev->common.module = (struct hw_module_t *)module; 68: dev->common.close = led_demo_close; 69: dev->set_on = led_demo_set_on; 70: dev->set_off = led_demo_set_off; 71:   72: if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) 73: { 74: ALOGE("Failed to open device %s ---- %s/n.", DEVICE_NAME, strerror(errno)); 75: free(dev); 76: return -EFAULT; 77: } 78:   79: *device = &(dev->common); 80:   81: ALOGE("Open device file %s successfully.", DEVICE_NAME); 82:   83: } 84:   85: return -EFAULT; 86: } 87:   88: static int led_demo_close(struct hw_device_t* device) 89: { 90: struct led_demo_device_t *led_device = (struct led_demo_device_t *)device; 91: if(led_device) 92: { 93: close(led_device->fd); 94: free(led_device); 95: } 96:   97: return 0; 98: } 99:   100: static int led_demo_set_on(struct led_demo_device_t *dev, int val) 101: { 102: if(!dev) 103: { 104: ALOGE("Null dev pointer."); 105: return -EFAULT; 106: } 107: 108: if(ioctl(dev->fd, LED_ON, val) < 0) 109: { 110: ALOGE("ioctl error --- %s.", strerror(errno)); 111: return -EFAULT; 112: } 113:   114: return 0; 115: 116: } 117:   118: static int led_demo_set_off(struct led_demo_device_t *dev, int val) 119: { 120: if(!dev) 121: { 122: ALOGE("Null dev pointer."); 123: return -EFAULT; 124: } 125: 126: if(ioctl(dev->fd, LED_OFF, val) < 0) 127: { 128: ALOGE("ioctl error --- %s.", strerror(errno)); 129: return -EFAULT; 130: } 131: 132: return 0; 133: 134: } 135:     hardware/libhardware/modules/led_demo_hal/Android.mk 1: LOCAL_PATH := $(call my-dir) 2: include $(CLEAR_VARS) 3: LOCAL_MODULE_TAGS := optional 4: LOCAL_PRELINK_MODULE := false 5: LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw 6: LOCAL_SHARED_LIBRARIES := liblog 7: LOCAL_SRC_FILES := led_demo_hal.cpp 8: LOCAL_MODULE := led_demo_hal.default 9: include $(BUILD_SHARED_LIBRARY)   编写完成后,在Android源码的顶层目录执行: mmm ./hardware/libhardware/modules/led_demo_hal/ 最终out/target/product/tiny4412/system/lib/hw/目录下得到一个led_demo_hal.default.so文件。 下面处理一下硬件设备访问权限问题 在硬件抽象层模块中,我们是调用open函数来打开对应的设备文件的,在默认情况下,只有root用户才有权限访问系统的设备文件。但是一般的应用程序是没有root用户权限的。 解决办法,赋予root之外的其他用户访问设别文件/dev/led_demo的权限。做法如下: 在Android源码顶层目录下,修改system/core/rootdir/ueventd.rc,添加如下内容: /dev/led_demo 0666 root root 修改了ueventd.rc文件后,需要重新编译Android源代码工程,编译时,文件system/core/rootdir/ueventd.rc会拷贝到out/target/product/tiny4412/root/下,并且最终打包在ramdisk.img中。对于友善之臂,执行完make -jN后,还需要执行./gen-img.sh脚本,然后在Android源码顶层目录中会生成ramdisk-u.img文件,利用MiniTools将其烧写到板子上。

四、编写Framework代码

  • 定义硬件访问服务接口
在frameworks/base/core/java/android/os/创建文件ILed_demo_service.aidl,内容如下: 1: package android.os; 2:   3: interface ILed_demo_service 4: { 5: void led_set_ON(int val); 6: void led_set_OFF(int val); 7: } 然后,修改frameworks/base/Android.mk 1: LOCAL_SRC_FILES += / 2: ...... 3: core/java/android/os/IVibratorService.aidl / 4: core/