阅读目录(Content)
参考资料:
《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
下面开始:
回到顶部(go to top)
一、编写驱动程序
分析tiny4412的原理图,看一下LED灯的位置:
可以知道,LED是低电平亮,高电平灭。
看一下,接到了Exynos4412的哪些引脚上了:
可以看到:
LED1 --------- GPM4_0
LED2 --------- GPM4_1
LED3 --------- GPM4_2
LED4 --------- GPM4_3
看一下Exynos4412的芯片手册,看一下GPM4的相关寄存器:
图中第二列表示的相对于基地址的偏移量,这里基地址是:0x11000000.
在芯片手册的Page288 ~ Page291对这些寄存器有更详细的介绍。
以GPM4_0引脚为例:
为了控制灯,[3:0]应设置为0x01,即输出模式
向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
1: config LED_DEMO
2: tristate
"Android Led Demo"
3: default n
4: help
5: This
is the led demo
for Android system.
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
1: ......
2:
3: obj-$(CONFIG_LED_DEMO) += android_led/
- 在内核顶层目录下执行make menuconfig,进入Device Drivers,将 Android Led Demo选择为*,然后保存配置退出。
注:执行上面这些操作之前,确保已经按照友善之臂的手册,成功编译了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:
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.
");
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.
");
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.
");
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.
");
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.
");
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.
");
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.
");
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.
");
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用户手册》
回到顶部(go to top)
二、编写代码测试驱动程序
在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.
");
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登陆板子,进行测试。
回到顶部(go to top)
三、编写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: