1.硬件原理图
LED灯在主板上,其原理图如下所示:
LED灯原理图
根据原理图,可知,只需要将GPIO口输出值为低电平,相应的LED灯就会点亮。
GPIO与LED对应图另外,在开发这一节的程序的时候,需要关注一张比较详细的Android的架构图(凑合看,尽管后来使用了ART):
Android系统架构图.png
2.JNI控制led灯
思路是实现一个java程序,用来访问C文件(其实是java与c的混搭,文件后缀为.c而已),而C库能够访问底层硬件,这样就做到App控制硬件了。
简单的java程序如下所示:
java程序
然后在MainActivity中添加该类,在开发板上运行,会报如下错误:
加载库报错到这一步,需要学习jni的相关知识了。
既然已经将c函数放在了java中,在jni中也就需要拿到该 函数,并且需要调用到。
我把代码贴出来,命名为led_jni.c:
#include "jni.h"
#include <stdlib.h>
#include <android/log.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
/**
* arm-linux-gcc -fPIC -shared led_jni.c -o libled.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include -nostdlib /home/wityuan/Desktop/android-5.0.2/prebuilts/ndk/8/platforms/android-14/arch-arm/usr/lib/libc.so -I /home/wityuan/Desktop/android-5.0.2/prebuilts/ndk/8/platforms/android-14/arch-arm/usr/include/ /home/wityuan/Desktop/android-5.0.2/prebuilts/ndk/8/platforms/android-14/arch-arm/usr/lib/liblog.so
*
*/
static jint fd;
jint Java_com_example_administrator_test1_ledControl_ledopen(JNIEnv *env , jobject thiz)
{
fd = open("/dev/leds",O_RDWR);
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_open...%d \r\n",fd);
if(fd<0)
return -1;
return 0;
}
void Java_com_example_administrator_test1_ledControl_ledclose(JNIEnv *env , jobject thiz)
{
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_close...");
close(fd);
}
jint Java_com_example_administrator_test1_ledControl_ledcontrol(JNIEnv *env , jobject thiz ,jint number, jint state)
{
int ret = ioctl(fd,state,number);
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_control:%d,%d...",number,state);
return ret;
}
static const JNINativeMethod methods[]={
{"Java_com_example_administrator_test1_ledControl_ledopen","()I",(void *)Java_com_example_administrator_test1_ledControl_ledopen},
{"Java_com_example_administrator_test1_ledControl_ledclose","()V",(void *)Java_com_example_administrator_test1_ledControl_ledclose},
{"Java_com_example_administrator_test1_ledControl_ledcontrol","(II)I",(void *)Java_com_example_administrator_test1_ledControl_ledcontrol},
};
JNIEXPORT jint JNICALL JNIOnLoad
(JavaVM *jvm, void *reserved)
{
JNIEnv* env = NULL;
jclass cls;
jint result=-1;
if( (*jvm)->GetEnv(jvm,(void**)&env , JNI_VERSION_1_4) != JNI_OK)
return JNI_ERR;
cls=(*env)->FindClass(env,"com/example/administrator/test1/led_control");
if(cls == NULL)
return JNI_ERR;
if((*env)->RegisterNatives(env,cls,methods,sizeof(methods)/sizeof(methods[0]))<0){
return JNI_ERR;
}
return JNI_VERSION_1_4;
}
将编译出来的so文件放到app目录的app/libs/armeabi下,接着修改build.gradle(app)下的内容,添加:
sourceSets{
...
defaultConfig {
...
ndk {
abiFilters 'armeabi', 'x86'//, 'armeabi-v7a', 'x86_64', 'arm64-v8a', mips, mips64...
}
}
sourceSets{
main{
jniLibs.srcDirs=['libs']
}
}
接下来,就需要编写c驱动程序了,这就需要使用内核源码linux-3.0.86。
操作步骤如下:
1.查看内核源码drivers/char/tiny4412_leds.c,并参照相关内容编写一个驱动,驱动命名为led_driver.c。
2.在drivers/char目录下,修改Makefile内容,添加一项:
obj-y+=led_driver.o
3.整个代码重新编译一次。
$ make zImage
4.将zImage烧写到系统中,然后启动开发板。
5.查看是否存在设备。
$ ls /dev/leds -al
crw-rw-rw- root root 10, 58 2018-04-21 06:35 leds
$ cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
21 sg
29 fb
81 video4linux
89 i2c
108 ppp
116 alsa
128 ptm
136 pts
153 spi
180 usb
188 ttyUSB
189 usb_device
204 ttySAC
248 hidraw
249 BaseRemoteCtl
250 media
251 ttyGS
252 ump
253 leds
254 rtc
6.安装app.
可以在logcat中看到如下打印信息:
logcat
目前,尽管有denied出现,但是仍然可以访问驱动程序,这个问题的影响在后面的驱动与应用出现了问题,再来解决。
led_driver.c驱动程序源码如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/device.h>
static int led_gpios[] = {
EXYNOS4212_GPM4(0),
EXYNOS4212_GPM4(1),
EXYNOS4212_GPM4(2),
EXYNOS4212_GPM4(3),
};
static int tiny4412_leds_open(struct inode *inode, struct file *file)
{
int i ;
for(i = 0 ; i < 4 ; i ++){
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], 1);
}
return 0;
}
static long tiny4412_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
if((cmd!= 0) && (cmd!=1))
return -EINVAL;
if(arg>4)
return -EINVAL;
gpio_set_value(led_gpios[arg], !cmd);
return arg;
}
static struct file_operations tiny4412_led_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tiny4412_leds_ioctl,
.open = tiny4412_leds_open,
};
static int major;
static struct class *cls;
static int __init tiny4412_leds_init(void)
{
major = register_chrdev(0,"leds",&tiny4412_led_dev_fops);
if(major <= 0){
printk("register_chrdev error\r\n");
return -1;
}
cls = class_create(THIS_MODULE,"led_cls");
device_create(cls,NULL,MKDEV(major,0),NULL,"leds");
return 0;
}
static void __exit tiny4412_leds_dev_exit(void) {
device_destroy(cls,MKDEV(major,0));
class_destroy(cls);
unregister_chrdev(major,"leds");
}
module_init(tiny4412_leds_init);
module_exit(tiny4412_leds_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wit_yuan driver.");
经过以上app-->jni---->c驱动程序,就能完成点亮led灯了。
完成这个流程之后,其实反过来能够发现,这样的一个结构并不是很好的分层结构。因为,APP直接操作JNI,JNI调用驱动程序,APP对驱动程序其实并不了解,最好的结构就是APP下层调用的是接口就可以了。这就是下一节要实践的内容。
网友评论