美文网首页
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