Tiny4412 led之NDK JNI实现

PC机平台:ubuntu 12.04.5

硬件平台:Tiny4412标准版+android5.0


Tiny4412硬件电路


从电路原理图可以知道LED灯连接到处理器的GPM4的0-3端口,且LED被上拉到3.3V的源,只有GPIO口输出低电平时就能点亮LED灯;


GPM4寄存器


只要把GPM4CON对应的区域设置为0x1且GPM4DAT对应bit设置为0,则IO口就会输出低电平;


编写led驱动程序

tiny4412_leds.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>


#define DEVICE_NAME		"tiny4412-leds"

#define GPM4CON		(0x11000000+0x02E0)
#define GPM4DAT 	(0x11000000+0x02E4)

#define GPM4_0	0
#define GPM4_1	1
#define GPM4_2	2
#define GPM4_3	3

#define GPM4_ON		0
#define GPM4_OFF 	1

//幻数
#define LEDS_MAGIC	'a'
#define LEDS_PGM4_0_ON		_IO(LEDS_MAGIC, 1)
#define LEDS_PGM4_0_OFF	_IO(LEDS_MAGIC, 2)
#define LEDS_PGM4_1_ON		_IO(LEDS_MAGIC, 3)
#define LEDS_PGM4_1_OFF	_IO(LEDS_MAGIC, 4)
#define LEDS_PGM4_2_ON		_IO(LEDS_MAGIC, 5)
#define LEDS_PGM4_2_OFF	_IO(LEDS_MAGIC, 6)
#define LEDS_PGM4_3_ON		_IO(LEDS_MAGIC, 7)
#define LEDS_PGM4_3_OFF	_IO(LEDS_MAGIC, 8)


unsigned int *gpm4_con = NULL;
unsigned int *gpm4_dat = NULL;


/* 设置IO口输出电平 */
static void set_gmp4_out(unsigned char cpm4_n, unsigned char status) 
{
	unsigned int temp;
	
	temp = readl(gpm4_dat);
	
	if (status == GPM4_ON) 
		temp &=  ~(0x1<<cpm4_n); 
	else
		temp |= 0x1<<cpm4_n;
	
	writel(temp, gpm4_dat);
}


static long tiny4412_leds_ioctl(struct file *file, unsigned int cmd,
			    unsigned long arg)
{
	switch (cmd) {
	case LEDS_PGM4_0_ON:  set_gmp4_out(GPM4_0, GPM4_ON);
		break;
	case LEDS_PGM4_0_OFF: set_gmp4_out(GPM4_0, GPM4_OFF);
		break;
	case LEDS_PGM4_1_ON:  set_gmp4_out(GPM4_1, GPM4_ON);
		break;
	case LEDS_PGM4_1_OFF: set_gmp4_out(GPM4_1, GPM4_OFF);
		break;
	case LEDS_PGM4_2_ON:  set_gmp4_out(GPM4_2, GPM4_ON);
		break;
	case LEDS_PGM4_2_OFF: set_gmp4_out(GPM4_2, GPM4_OFF);
		break;
	case LEDS_PGM4_3_ON:  set_gmp4_out(GPM4_3, GPM4_ON);
		break;
	case LEDS_PGM4_3_OFF: set_gmp4_out(GPM4_3, GPM4_OFF);
		break;
	defautl :
		break;
	}
	
	return 0;
}


static const struct file_operations tiny4412_leds_fops = {
	.owner		= THIS_MODULE,
	.unlocked_ioctl	= tiny4412_leds_ioctl,
};


static struct miscdevice tiny4412_leds_miscdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &tiny4412_leds_fops,
};


static int tiny4412_leds_init(void)	
{
	unsigned int data;	
	unsigned int ret;
	
	//IO初始化,将物理地址映射为虚拟地址
	gpm4_con = ioremap(GPM4CON, 4);
	gpm4_dat = ioremap(GPM4DAT, 4);
	if (!gpm4_con || !gpm4_con) {
		printk("ioremap faild!\n");
		goto error1; 
	}
	
	/* 将GPM4[0]-GPM4[3]设置为输出 */
	data = readl(gpm4_con);  
	data &= ~((0xf<<12)|(0xf<<8)|(0xf<<4)|(0xf<<0)); 
	data |=  (0x1<<12)|(0x1<<8)|(0x1<<4)|(0x1<<0); 
	writel(data, gpm4_con); 

	ret = misc_register(&tiny4412_leds_miscdev);  //注册混杂设备驱动
	if (ret) {
		printk("misc_register faild!\n");
		goto error2;
	}
	
	printk("tiny4412_leds_init!\n");
	
	return 0;

	
error2:
	iounmap(gpm4_con);
	iounmap(gpm4_dat);

error1:
	return -ENOMEM;
		
}


static void tiny4412_leds_exit(void)
{
	unsigned int data;

	misc_deregister(&tiny4412_leds_miscdev);
	
	data = readl(gpm4_dat);
	data |=  (0x1<<3)|(0x1<<2)|(0x1<<1)|(0x1<<0); 
	writel(data, gpm4_dat);
	
	iounmap(gpm4_con);
	iounmap(gpm4_dat);
	printk("tiny4412_leds_exit!\n");
}


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chen Jinpeng");


module_init(tiny4412_leds_init);
module_exit(tiny4412_leds_exit);


编写LED测试程序

led_oper.c

#include <stdio.h>   
#include <sys/types.h>  
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/ioctl.h>


#define DEVICE_NAME		"/dev/tiny4412-leds"

//幻数
#define LEDS_MAGIC	'a'
#define LEDS_PGM4_0_ON		_IO(LEDS_MAGIC, 1)
#define LEDS_PGM4_0_OFF	_IO(LEDS_MAGIC, 2)
#define LEDS_PGM4_1_ON		_IO(LEDS_MAGIC, 3)
#define LEDS_PGM4_1_OFF	_IO(LEDS_MAGIC, 4)
#define LEDS_PGM4_2_ON		_IO(LEDS_MAGIC, 5)
#define LEDS_PGM4_2_OFF	_IO(LEDS_MAGIC, 6)
#define LEDS_PGM4_3_ON		_IO(LEDS_MAGIC, 7)
#define LEDS_PGM4_3_OFF	_IO(LEDS_MAGIC, 8)


int main (int argc, char *argv[]) 
{
	int leds_fd = 0;
	int cmd;
	
	if  (argc < 2) {
		printf("please entry the correct operation parameter! \n");
		return 0;
	}

	leds_fd = open(DEVICE_NAME, O_RDWR);
	if (leds_fd == -1) {
		printf("open device faild! \n");
		return 0;
	} 

	cmd = atoi(argv[1]);		//把终端上收到的字符串命令转换成整型
	//printf("argv:%s \n", argv[1]);
        /* 执行 ./led_oper 11 点亮LED1 , 执行./led_oper 10 关闭LED1,其他LED灯以此类推 */
	switch (cmd) {
	case 10:  ioctl(leds_fd, LEDS_PGM4_0_OFF);
		break;
	case 11:  ioctl(leds_fd, LEDS_PGM4_0_ON);
		break;
	case 20:  ioctl(leds_fd, LEDS_PGM4_1_OFF);
		break;
	case 21:  ioctl(leds_fd, LEDS_PGM4_1_ON);
		break;
	case 30:  ioctl(leds_fd, LEDS_PGM4_2_OFF);
		break;
	case 31:  ioctl(leds_fd, LEDS_PGM4_2_ON);
		break;
	case 40:  ioctl(leds_fd, LEDS_PGM4_3_OFF);
		break;
	case 41:  ioctl(leds_fd, LEDS_PGM4_3_ON);
		break;
	defautl :
		break;
	}

	close(leds_fd);

	return 0;
}

编写Makefile

obj-m := tiny4412_leds.o

KDIR := /home/workplace/Tiny4412/linux-3.0.86/   # arm

all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- 
	arm-linux-gcc -static led_oper.c -o led_oper
	
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul* *oper

编译完成后将,tiny4412_leds.ko和led_oper使用adb push到SD卡或使用手机助手软件拷贝到SD中;

安装完tiny4412_leds.ok后,要执行chmod 666 /dev/tiny4412-leds让应用程序能够访问该节点;   然后使用led_oper测试led驱动程序,然而在使用led_oper时会发现权限不够,chmod 777之后也是出现权限不够的提示,原因是SD卡有没可执行的权限,应该必须修改挂载权限,参考友善的用户手册还是无法重新挂载SD卡分区,只能将led_oper拷贝到/data目录下应该/data目录有可执行的权限,然后就可以验证led驱动了;


编写JNI和Android

创建android项目


编写JNI接口

创建JNI接口类tiny4412Leds.java


编写JNI接口,同时加载动态链接库

package com.example.tiny4412_leds;

public class tiny4412Leds {
	static {
		System.loadLibrary("tiny4412-leds");
	}
	
	public native int ledsOperation(int ledNum, boolean status);  //接口定义了两个参数:LED编号和开关状态
}
创建了一个ledsOperation的JNI接口,通过该接口能够访问linux下的设备节点;
注意,这里的函数声明要加上native关键字。动态链接库就是我们将要编译生成的*.so文件,编译生成的.so文件会自动加上lib前缀,加载的名称不需要lib前缀,系统在加载的时候回自动帮我们加上前缀。

编译JNI接口头文件

在dos命令行进入tiny4412_leds\src\com\example\tiny4412_leds目录下,执行javac tiny4412Leds.java生成tiny4412Leds.class



后退到src目录,执行javah -jni 类名加包名 (类名和包名建议直接copy)


编译之后就会在src目录下生成com_example_tiny4412_leds_tiny4412Leds.h文件



com_example_tiny4412_leds_tiny4412Leds.h文件就是对应于上面定义的Java接口的C/C++头文件。打开这个文件,可以看到系统已经为我们自动完成了接口函数的声明:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_tiny4412_leds_tiny4412Leds */

#ifndef _Included_com_example_tiny4412_leds_tiny4412Leds
#define _Included_com_example_tiny4412_leds_tiny4412Leds
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_tiny4412_leds_tiny4412Leds
 * Method:    ledsOperation
 * Signature: (IZ)I
 */
JNIEXPORT jint JNICALL Java_com_example_tiny4412_1leds_tiny4412Leds_ledsOperation
  (JNIEnv *, jobject, jint, jboolean);

#ifdef __cplusplus
}
#endif
#endif
这个接口函数的命名方式只是在前面加上了Java包名。

用C实现JNI

有了JNI的C/C++头文件,就可以在C层实现JNI接口了。首先在工程目录下创建一个jni目录,这个目录就是专门用来放C/C++代码的。把com_example_tiny4412_leds_tiny4412Leds.h文件复制到jni目录下,并在这里创建一个com_example_tiny4412_leds_tiny4412Leds.c文件,拷贝com_example_tiny4412_leds_tiny4412Leds.h里的函数接口到com_example_tiny4412_leds_tiny4412Leds.c中并编写实现;


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include "com_example_tiny4412_leds_tiny4412Leds.h"


#define DEVICE_NAME		"/dev/tiny4412-leds"

#define LED_0		1
#define LED_1		2
#define LED_2		3
#define LED_3		4

//幻数
#define LEDS_MAGIC	'a'
#define LEDS_PGM4_0_ON		_IO(LEDS_MAGIC, 1)
#define LEDS_PGM4_0_OFF	_IO(LEDS_MAGIC, 2)
#define LEDS_PGM4_1_ON		_IO(LEDS_MAGIC, 3)
#define LEDS_PGM4_1_OFF	_IO(LEDS_MAGIC, 4)
#define LEDS_PGM4_2_ON		_IO(LEDS_MAGIC, 5)
#define LEDS_PGM4_2_OFF	_IO(LEDS_MAGIC, 6)
#define LEDS_PGM4_3_ON		_IO(LEDS_MAGIC, 7)
#define LEDS_PGM4_3_OFF	_IO(LEDS_MAGIC, 8)

/*
 * Class:     com_example_tiny4412_leds_tiny4412Leds
 * Method:    ledsOperation
 * Signature: (II)I
 */
jint JNICALL Java_com_example_tiny4412_1leds_tiny4412Leds_ledsOperation
	(JNIEnv *env, jobject obj, jint ledsNum, jboolean status)   
{
	int leds_fd = 0;

	leds_fd = open(DEVICE_NAME, O_RDWR);  //打开设备节点
	if (leds_fd == -1) {
		return 1;  
	}

	switch (ledsNum) {
	case LED_0:
		if (status)
			ioctl(leds_fd, LEDS_PGM4_0_ON);
		else
			ioctl(leds_fd, LEDS_PGM4_0_OFF);
		break;
	case LED_1:
		if (status)
			ioctl(leds_fd, LEDS_PGM4_1_ON);
		else
			ioctl(leds_fd, LEDS_PGM4_1_OFF);
		break;
	case LED_2:
		if (status)
			ioctl(leds_fd, LEDS_PGM4_2_ON);
		else
			ioctl(leds_fd, LEDS_PGM4_2_OFF);
		break;
	case LED_3:
		if (status)
			ioctl(leds_fd, LEDS_PGM4_3_ON);
		else
			ioctl(leds_fd, LEDS_PGM4_3_OFF);
		break;
	defautl :
		break;
	}

	close(leds_fd);

	return 0;  //操作成功返回0
}

创建mk文件

JNI实现了之后就要把C/C++代码编译成动态链接库.so文件,这样Java程序才能调用JNI的接口,要编译so文件,需要写Android.mk文件。

先在工程目录的jni下Android.mk文件:

然后打开Android.mk文件在里面输入如下内容:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_LDLIBS := -llog
LOCAL_MODULE := tiny4412-leds
LOCAL_SRC_FILES := com_example_tiny4412_leds_tiny4412Leds.c

include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE为模块名,要跟tiny4412Leds类中的System.loadLibrary("tiny4412-leds");保持一致;
LOCAL_SRC_FILES 对应C代码文件;

编译os动态链接库

写完了mk文件就可以开始编译C/C++代码了。可以下windows下编译,也可以在linux下编译;而编译os库需要通过NDK工具进行编译;

Windows下Android NDK的安装

在网上下载 android-ndk-r9d-windows-x86_64.zip ,操作系统多少位的就下载相应的;然后解压,存放的路径不能有中文字符和空格;在环境变量path中添加ndk的目录;

在dos命令行下输入:ndk-bulid


输出未找到应用项目路径即表明开发环境配置成功;

Windows下编译os库

然后进入项目顶层目录,执行ndk-build 


在将libs目录下生成armeabi目录,并armeabi目录下生成so文件



linux下Android NDK的安装

在网上下载 android-ndk-r10d-linux-x86_64.bin ,操作系统多少位的就下载相应的;

拷贝到自己的工作目录下然后执行 ./android-ndk-r10d-linux-x86_64.bin 就把文件解压好了;


执行export PATH="$PATH:/home/chenjp/workplace/android-ndk-r10d" 将路径添加到环境变量中

执行ndk-build


输出未找到应用项目路径即表明开发环境配置成功;

linux下编译os库

进入项目的jni目录,然后执行ndk-build


在将libs目录下生成armeabi目录,并armeabi目录下生成so文件


生成其他平台的so库

有时候,我们能够看到其他一些apk包中的lib目录下有armeabi armeabi-v7a mips x86等其他平台的so库;

而编译器默认是生成armeabi库,如果想生成其他平台的库,可以执行:ndk-build APP_ABI="armeabi armeabi-v7a x86" 或 APP_ABI := all,这样就能生成其他平台的库文件了;




如果不想每次都加上APP_ABI参数,可以在jni目录下创建Application.mk 文件;


Application.mk 文件中写入 APP_ABI := armeabi armeabi-v7a mips x86  或  APP_ABI := all;


这样直接使用ndk-build指令就可以生成其他平台的so库了;


到这jni编译也介绍完毕了,接下来就编写android应用程序了;

编写android应用程序

编写activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <CheckBox 
        android:id="@+id/leds1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LED1"/>
    
    <CheckBox 
        android:id="@+id/leds2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LED2"/>
    
    <CheckBox 
        android:id="@+id/leds3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LED3"/>

    <CheckBox 
        android:id="@+id/leds4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LED4"/>
    
</LinearLayout>

编写MainActivity.java

package com.example.tiny4412_leds;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.Toast;

public class MainActivity extends Activity {

	final int LED_0 = 1;
	final int LED_1 = 2;
	final int LED_2 = 3;
	final int LED_3 = 4;

	tiny4412Leds led = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		led = new tiny4412Leds();
		
		CheckBox leds1 = (CheckBox) findViewById(R.id.leds1);
		CheckBox leds2 = (CheckBox) findViewById(R.id.leds2);
		CheckBox leds3 = (CheckBox) findViewById(R.id.leds3);
		CheckBox leds4 = (CheckBox) findViewById(R.id.leds4);

		leds1.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				// TODO Auto-generated method stub	
				if (led.ledsOperation(LED_0, isChecked)  == 0) {
					Toast.makeText(MainActivity.this, "Led1操作成功", Toast.LENGTH_SHORT).show();
				} else {
					Toast.makeText(MainActivity.this, "操作失败", Toast.LENGTH_SHORT).show();
				}
				
			}
		});

		leds2.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				// TODO Auto-generated method stub		
				if (led.ledsOperation(LED_1, isChecked)  == 0) {
					Toast.makeText(MainActivity.this, "Led2操作成功", Toast.LENGTH_SHORT).show();
				} else {
					Toast.makeText(MainActivity.this, "操作失败", Toast.LENGTH_SHORT).show();
				}
			}
		});

		leds3.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				// TODO Auto-generated method stub
				if (led.ledsOperation(LED_2, isChecked)  == 0) {
					Toast.makeText(MainActivity.this, "Led3操作成功", Toast.LENGTH_SHORT).show();
				} else {
					Toast.makeText(MainActivity.this, "操作失败", Toast.LENGTH_SHORT).show();
				}
			}
		});
		
		leds4.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				// TODO Auto-generated method stub
				if (led.ledsOperation(LED_3, isChecked)  == 0) {
					Toast.makeText(MainActivity.this, "Led4操作成功", Toast.LENGTH_SHORT).show();
				} else {
					Toast.makeText(MainActivity.this, "操作失败", Toast.LENGTH_SHORT).show();
				}
			}
		});			
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}
应用程序使用四个CheckBox控件,对LED进行操作;

运行结果:



点击按键复选框,可以从开发板上看到对应的led会被点亮;

至此led之jni实现编写完毕,如果操控底层硬件能够通过简单的open和ioctl等实相对比较简单的操作完成时,可以通过jni机制直接调用c/c++代码,实现就硬件设备的操作;就无需去编写繁琐的硬件访问服务程序;



JNI部分参考:http://blog.sina.com.cn/s/blog_4298002e01013zk8.html




  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值