A20_Android平台驱动在 HAL层、JNI层和APP层的开发

本文介绍了Android平台驱动开发,从HAL层、JNI层到APP层的详细步骤。首先强调了Linux系统下硬件驱动的重要性,接着通过实例展示了Linux设备驱动的配置和编写,包括SPI接口的IO参数设置。然后,阐述了JNI作为Android与Linux交互的桥梁,以及在JNI层的开发过程。最后,讲解了如何进行Android App的开发,包括布局文件的创建和权限设置,以实现对硬件设备的控制。
该分享记录了实现通过安卓app界面的button来控制单个LED灯亮灭的过程和方法。可以概述为:首先查找用户手册,确定目标IO口的寄存器等参数;第二步编写IO口驱动程序,并将驱动模块编译进内核;第三步编写jni代码,编译生成.so;第四步在eclipse工程中配置、添加功能代码,烧录并执行。

心得:

(1).如果只偏重于硬件和计算,那么安卓系统可以看做一个外壳,一个注重于展现的外壳;

(2).linux下面有很多好玩的东西,这里的世界只有C和makefile,更单纯;

(3).ubuntu作为一种流行的linux系统,可以做很多原来认为不能做或者做不好的事情,比如安卓app的开发,我现在已经开始用ubuntu写ppt和发送邮件了,如果开始做应用程序的开发将会对linux的认识更深入一层。

(4).所谓的jni,也是一种代码,可以理解为android和linux交流的窗口。

(5).linux下的ARM设备的IO配置是升级版的单片机IO配置,要先找到寄存器名称、offset、对应位,然后配置模式,与cc3200(arm  cortex-m4内核)的IO配置类似。

(6).Linux系统把任何设备都抽象位文件,设备文件分为:字符设备,网络设备、块设备,这些设备都以文件的形式存在于/dev路径下,若dev路径下不存在一个设备对应的文件,则表示该设备没有加载成功。所以驱动程序或者jni层的代码可一看到大量的open、read、write、close等形式的操作。

第一步:确定IO参数

    由于前面开发时使用了spi0的cs引脚,因此本实验依然采用spi0_cs0引脚,端口号为PI10。

寄存器:PI_CFG1      offset:0x124

对应位:10:8

配置模式:output:001

数据寄存器:PI_DAT   offset:0x130

2016-06-08 19:06:18的屏幕截图.png

2016-06-08 19:07:02的屏幕截图.png

2016-06-08 19:07:16的屏幕截图.png

2016-06-08 19:20:29的屏幕截图.png

第二步:IO驱动模块

驱动代码(放在lichee/linux3.3/drivers路径下):

/*************************************************************
 ***********************ctl_io_driver*************************
**************************************************************/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/slab.h>
#include<linux/device.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <asm/uaccess.h>

/*configs for spi1_cs0*/
static volatile unsigned long *pccfg2 = NULL;
static volatile unsigned long *pcdat0 = NULL;
/*configs for 485_rse*/
static volatile unsigned long *picfg1 = NULL;//PI14
static volatile unsigned long *pidat0 = NULL;

static volatile unsigned long *picfg2 = NULL;//PI10
static volatile unsigned long *pidat1 = NULL;

static volatile unsigned long *pgcfg0 = NULL;//PG04
static volatile unsigned long *pgdat0 = NULL;

/*configs for spi1_cs0*/
/*0x01C208+addr*/
#define vPC_CFG2 0x01C20850
#define vPC_DAT0 0x01c20858

#define vPI_CFG1 0x01C20924      //0x01C208+0x124
#define vPI_DAT0 0x01c20930      //0x01C208+0x130

#define vPG_CFG0 0x01C208d8
#define vPG_DAT0 0x01c208e8


struct ctl_io_driver
{
    unsigned int dev_major;
    struct class *ctl_io_class;
    struct device *ctl_io_device;
    struct cdev *ctl_io_cdev;
    dev_t  devno;
    unsigned int rval;
};

static struct ctl_io_driver *a20_driver;

static int ctl_io_open(struct inode *inode, struct file *file)
{
//    pr_info(KERN_INFO "ctl_io open ok\n");    

    /*spi_cs:output*/
    *pccfg2 &= ~(0xf<<12);
    *pccfg2 |=  (0x1<<12);

    /*485_rse:output*/
    *picfg1 &= ~(0xf<<24);  //PI14
    *picfg1 |=  (0x1<<24);

    *picfg1 &= ~(0xf<<8);   //PI10 对应寄存器bit的较低位 (10:8)
    *picfg1 |=  (0x1<<8);   //设置为输出模式

    *pgcfg0 &= ~(0xf<<16);//PG04
    *pgcfg0 |=  (0x1<<16);
    
    return 0;
}

static ssize_t ctl_io_read(struct file * file, const char __user * buf, size_t size, loff_t * ppos)
{
//    printk(KERN_INFO "ctl_io read ok\n");
    
    return size;
}

static ssize_t ctl_io_write(struct file * file, const char __user * buf, size_t size, loff_t * ppos)
{
    if(copy_from_user(&a20_driver->rval, buf, size))
    {
        printk(KERN_ERR "failed to copy from user!\n");
        return -EINVAL;
    }
    
    switch(a20_driver->rval)
    {
        /*config for spi_cs*/
        /*down*/
        case 1:
        {
            *pcdat0 &= ~((0x1<<19));   
            break;
        } 
        /*up*/
    case 2:   
        {
            *pcdat0 |= ((0x1<<19));
            break;
        }   
        /*config for 485_rse*/
        /*down*/
        case 3:
        {
            *pidat0 &= ~((0x1<<14));   
            break;
        }  
        /*up*/
    case 4:   
        {
            *pidat0 |= ((0x1<<14));
            break;
        } 
        /*down*/
        case 5:
        {
            *pgdat0 &= ~((0x1<<4));   
            break;
        }  
        /*up*/
    case 6:   
        {
            *pgdat0 |= ((0x1<<4));
            break;
        } 

        /*down*/
        case 7:
        {
            *pidat1 &= ~((0x1<<10));   //置为低电平
            break;
        }  
        /*up*/
    case 8:   
        {
            *pidat1 |= ((0x1<<10));    //置为高电平
            break;
        } 
    }

    return size;
}

//函数名称的映射
struct file_operations open_file = {
    .owner = THIS_MODULE,
    .open = ctl_io_open,
    .read = ctl_io_read,
    .write= ctl_io_write,
};

/*int cdev struct*/
static void setup_cdev_init(void)
{
    a20_driver->ctl_io_cdev = cdev_alloc();
    cdev_init(a20_driver->ctl_io_cdev, &open_file);
    cdev_add(a20_driver->ctl_io_cdev, a20_driver->devno, 1);    
}

static int __init ctl_io_test_init(void)
{
    int ret;
//    printk(KERN_INFO "ctl_io_init ok!\n");
    
    a20_driver = kmalloc(sizeof(struct ctl_io_driver), GFP_KERNEL);
    if(NULL == a20_driver)
    {
        printk(KERN_ERR "faild to malloc memory\n");
        return -ENOMEM;
    }

    a20_driver->dev_major = 0;    
    if(a20_driver->dev_major)
    {
        a20_driver->devno = MKDEV(100,0);
        ret = register_chrdev_region(a20_driver->devno, 1, "ctl_io_module");
        if(ret < 0)
        {
            printk(KERN_INFO "failed to register chrdev region\n");
            goto register_err;
        }
    }
    else
    {
        a20_driver->devno = MKDEV(a20_driver->dev_major,0);
        ret = alloc_chrdev_region(&a20_driver->devno, 0,1, "ctl_io_module");
        if(ret < 0)
        {
            printk(KERN_INFO "failed to register chrdev region\n");
            goto register_err;
        }
    }
    
    setup_cdev_init();
    
    a20_driver->ctl_io_class = class_create(THIS_MODULE, "ctl_io_class");
    if (IS_ERR(a20_driver->ctl_io_class))
    {
        ret = PTR_ERR(a20_driver->ctl_io_class);
        goto class_err;
    }
    
    a20_driver->ctl_io_device = device_create(a20_driver->ctl_io_class, NULL, a20_driver->devno, NULL, "ctl_io");
    if (IS_ERR(a20_driver->ctl_io_device))
    {
        ret = PTR_ERR在lichee/linux3.3/drivers/ctl_io(a20_driver->ctl_io_device);
        goto device_err;
    }

    /*map the addr*/
    pccfg2 = ioremap(vPC_CFG2, 0x10);
    pcdat0 = ioremap(vPC_DAT0, 0x10);
    picfg1 = ioremap(vPI_CFG1, 0x10);
    pidat0 = ioremap(vPI_DAT0, 0x10);

    picfg2 = ioremap(vPI_CFG1, 0x10);
    pidat1 = ioremap(vPI_DAT0, 0x10);

    pgcfg0 = ioremap(vPG_CFG0, 0x10);
    pgdat0 = ioremap(vPG_DAT0, 0x10);    

    return 0;
    
device_err:
    class_destroy(a20_driver->ctl_io_class);
class_err:
    unregister_chrdev_region(a20_driver->devno, 1);
    cdev_del(a20_driver->ctl_io_cdev);    
register_err:
    kfree(a20_driver);
    return ret;    
}

static void __exit ctl_io_test_exit(void)
{
    printk(KERN_INFO "ctl_io_exit ok!\n");
    
    unregister_chrdev_region(a20_driver->devno, 1);
    cdev_del(a20_driver->ctl_io_cdev);
    device_destroy(a20_driver->ctl_io_class, a20_driver->devno);
    class_destroy(a20_driver->ctl_io_class);
    iounmap(pccfg2);
    iounmap(pcdat0);

    iounmap(picfg1);
    iounmap(pidat0);

    iounmap(picfg2);
    iounmap(pidat1);

    iounmap(pgcfg0);
    iounmap(pgdat0);
    kfree(a20_driver);
}

module_init(ctl_io_test_init);
module_exit(ctl_io_test_exit);

MODULE_LICENSE("GPL");

Makefile代码:

lichee/linux3.3/drivers下的Makefile,在最后添加:在lichee/linux3.3/drivers/ctl_io

obj-y +=ctl_io.o

借鉴下面的方法,编译生成新的固件,并升级开发板。

http://blog.youkuaiyun.com/penglijiang/article/details/45457627

再次开机后,会在/dev下生成名为ctl_io的文件节点。


第三步:JNI层开发

1.新建eclipse项目

1350974429_5568.png

1350974564_6494.png

1350974614_5472.png

2.在Led.java中添加 public static native String led(int ctl);

3.在workspace的工程目录下面新建jni文件夹

2016-06-08 19:49:31的屏幕截图.png

4.在jni下面新建led.c文件

2016-06-08 19:50:51的屏幕截图.png

#include <jni.h>  
#include <stdio.h>     
#include <stdlib.h>     
#include <fcntl.h>  
#include <unistd.h> 
#include <sys/types.h>    
#include <sys/ioctl.h>    
//#include <linux/spi/spidev.h> 
#include <android/log.h>  


#define LOG_TAG "LED"       //android logcat  
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__    )  
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS_    _)  

int pullup = 8;
int pulldown = 7;
 
jstring JNICALL Java_com_example_led_Led_led(JNIEnv *env, jclass thiz,jint ctl)   
{    
    int fd ;
    fd = open("/dev/ctl_io",O_RDWR); 
    if(fd < 0)    
        return (*env)->NewStringUTF(env, "Can't open /dev/ctl_io!\n");    

    jstring xstring;
    jint ret;
    if(ctl == 1)
    {
            xstring = "onnnnnnnnn";
            ret = write(fd, &pullup, 4);
        if(ret < 0)
        {
            close(fd);
            return (*env)->NewStringUTF(env, "write  pullup failed\n");
        }
        else
        {
            close(fd);
            return (*env)->NewStringUTF(env, "write  pullup ok\n");
        }
    } 
    else
    {
            xstring = "offffffffff";
            ret = write(fd, &pulldown, 4); 
        if(ret < 0)
        {
            close(fd);
            return (*env)->NewStringUTF(env, "write pulldown failed\n");
        }
        else 
        {
            close(fd);
            return (*env)->NewStringUTF(env, "write pulldown ok\n");
        }

    } 

    //close(fd);    
  
    //return (*env)->NewStringUTF(env, xstring);    
} 

5.在jni下面新建Android.mk文件

LOCAL_PATH     := $(call my-dir)
include $(CLEAR_VARS)
LIB_PATH := $(LOCAL_PATH)/../../libs/armeabi
LOCAL_MODULE    := led

LOCAL_SRC_FILES := \
    led.c
LOCAL_C_INCLUDES := $(MY_ANDROID_SOURCE)/frameworks/base/core/jni/android/graphics \ 

LOCAL_ARM_MODE := arm

include $(BUILD_SHARED_LIBRARY)

6.新建Application.mk文件

APP_PLATFORM := android-20

APP_ABI := armeabi-v7a

7.在工程目录下执行ndk-build生成.so文件

2016-06-08 19:55:16的屏幕截图.png

2016-06-08 19:56:11的屏幕截图.png

生成相应头文件#javah -classpath bin/classes -d jni com.example.led.Led

第四步 安卓App开发

java代码:

package com.example.led;  
  
import android.app.Activity;    
import android.content.Intent;    
import android.net.Uri;    
import android.os.Bundle;    
import android.view.View;    
import android.widget.Button;   
import android.util.Log;  
import android.view.Menu;
import android.widget.CompoundButton.OnCheckedChangeListener; 
import android.widget.TextView; 

public class Led extends Activity {  
  
    private Button button1;
    private TextView tvvv;
    private boolean index = false;
    
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.led);  
        button1 = (Button)findViewById(R.id.btn1);
        button1.setText("off");
        //led(0,0); 
    
            
        tvvv = (TextView)findViewById(R.id.textView1);
        
        button1.setOnClickListener(new Button.OnClickListener()
        {
            private String tt;
            public void onClick(View v)
            {
                if(index)
                {
                    tt = led(1);    
                    tvvv.setText(tt);   //显示驱动返回的字符,调试用
                    button1.setText("on");
                    index = false;
                }
                else 
                {
                    tt = led(0);     
                    tvvv.setText(tt);
                    button1.setText("off");
                    index = true;
                }                             
                
            }    
            
        } );
            
  
    }  
    
    public boolean onCreateOptionsMenu(Menu menu) {  
        getMenuInflater().inflate(R.menu.led, menu);  
        return true;  
    }      
    
    public static native String led(int ctl);  
    
    static   
    {  
        System.loadLibrary("led");  
    }   
    
} 

布局文件:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        xmlns:tools="http://schemas.android.com/tools"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"   
        android:orientation="vertical"  >    
      
      
        <Button   
                  android:id="@+id/btn1"        
                  android:layout_width="140dip"          
                  android:layout_height="wrap_content"                   
                  android:layout_gravity="center_horizontal"   
                  />

        <TextView
            android:id="@+id/textView1"
            android:layout_width="252dp"
            android:layout_height="wrap_content"
            android:text="TextView" />
      
    </LinearLayout>  

如果提示打开文件失败,则可能是没有操作/dev下ctl_io的权限,可通过adb shell进入dev路径执行:chmod 777 ctl_io

将apk安装至开发板,执行效果如下:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值