美文网首页
3.LED灯简单驱动程序编写流程(1)

3.LED灯简单驱动程序编写流程(1)

作者: wit_yuan | 来源:发表于2018-04-01 21:17 被阅读0次

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下层调用的是接口就可以了。这就是下一节要实践的内容。

相关文章

网友评论

      本文标题:3.LED灯简单驱动程序编写流程(1)

      本文链接:https://www.haomeiwen.com/subject/xdgacftx.html