Android项目——触摸按键控制LED
一、Android Studio应用编程
1.应用程序界面layout对应的界面是activity_main.xml,后台对应的java文件是MainActivity.java,修改activity_main.xml来修改UI显示效果,
点击UI上的控件的事件处理由MainActivity.java完成,一般放在onCreate()函数内。
2.当拖动修改界面的时候,对应的activity_main.xml会被自动修改。layout上面的控件的排布应该由layout的属性来决定。
3.视频上使用的AS的模拟器是Nexus 5,我Tiny4412上使用的Nexus One模拟器。
4.System.loadLibrary("hardcontrol"); 这个库应该放在开发板上的/vendor/lib或/system/lib下,也可以将这个库打包进应用程序里面。
5.修改这个xml文件后编译一下工程,可能会使AS根据xml文件生成一些java源代码。
6.比如下面编译报错Toast找不到,然后在Toast上弹出提示是不是XXX, 按Ctrl+enter即可确认,然后就可以编译成功了。
Toast.makeText(getApplicationContext(), "LED2 on",Toast.LENGTH_SHORT).show();
7.其实这个App可以在手机上运行,前提是手机开启了调试模式,而且Android版本要超过4.0,因为在建立此工程时指定了最小SDK版本号为4.0。
二、使用到的工程文件和更改
1.activity_main.xml注释
<?xml version="1.0" encoding="utf-8"?>
//默认是android.support.constraint.ConstraintLayout,居中排列,改为LinearLayout后控件线性排列
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" //添加它后由之前的控件水平排列变为垂直排列
tools:context=".MainActivity"> <TextView //首字母大写就表示它是一个类对象。Ctrl+单击:到这个类定义的位置处,Ctrl+h:查看这个类的继承关系。
android:layout_width="wrap_content" //表示显示信息的宽度,"wrap_content"表示宽度取决于其内容
android:layout_height="wrap_content" //其高度也是由其内容决定
android:text="SunFaliang coming!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" /> /* 下面是手动添加的部分 */
<Button //双击“Button”,然后按下Shift+Fn+F1会在浏览器中打开Button的使用帮助文档,可根据帮助文档编写Button事件的处理函数。
android:id="@+id/BUTTON" //给Button控件添加一个id,此后,MainActivity.java中就可以通过findViewById(R.id.BUTTON)来attach这个Button。
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="ALL ON"/> //这个Button上默认显示的内容 <CheckBox //输入"<",然后tab键,系统帮我们补全了宽度和高度的代码
android:id="@+id/LED1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="LED1"
android:onClick="onCheckboxClicked"/> <CheckBox
android:id="@+id/LED2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="LED2"
android:onClick="onCheckboxClicked"/> <CheckBox
android:id="@+id/LED3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="LED3"
android:onClick="onCheckboxClicked"/> <CheckBox
android:id="@+id/LED4"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="LED4"
android:onClick="onCheckboxClicked"/>
</LinearLayout>
2.MainActivity.java注释
/* 这个包的位置是AS自动生成的 */
package com.example.mm.app_0001_led_demo; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;
import com.example.mm.hardLibrary.*; public class MainActivity extends AppCompatActivity { private boolean ledon = false;
/*双击Button,Shift+Fn+F1可以在浏览器中打开其帮助文档*/
private Button button = null; private CheckBox checkBoxLed1 = null;
private CheckBox checkBoxLed2 = null;
private CheckBox checkBoxLed3 = null;
private CheckBox checkBoxLed4 = null; class MyButtonListener implements View.OnClickListener { //OnClickListener is a inner interface of View /* Ctrl + i 是复写快捷键,按下后复写哪个函数直接点击提示即可 */
@Override
public void onClick(View v) {
ledon = !ledon;
if (ledon) {
button.setText("ALL OFF");
checkBoxLed1.setChecked(true);
checkBoxLed2.setChecked(true);
checkBoxLed3.setChecked(true);
checkBoxLed4.setChecked(true);
for (int i = 0; i < 4; i++) {
HardControl.ledCtrl(i, 1);
}
} else {
button.setText("ALL ON");
checkBoxLed1.setChecked(false);
checkBoxLed2.setChecked(false);
checkBoxLed3.setChecked(false);
checkBoxLed4.setChecked(false);
for (int i = 0; i < 4; i++) {
HardControl.ledCtrl(i, 0);
}
}
}
} public void onCheckboxClicked(View view) {
// Is the view now checked?
boolean checked = ((CheckBox) view).isChecked(); // Check which checkbox was clicked
switch(view.getId()) {
case R.id.LED1:
if (checked) {
HardControl.ledCtrl(1, 1);
Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
} else {
HardControl.ledCtrl(1, 0);
Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
}
break;
case R.id.LED2:
if (checked) {
HardControl.ledCtrl(2, 1);
Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
} else {
HardControl.ledCtrl(2, 0);
Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
}
break;
case R.id.LED3:
if (checked) {
HardControl.ledCtrl(3, 1);
Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
} else {
HardControl.ledCtrl(3, 0);
Toast.makeText(getApplicationContext(), "LED3 off",Toast.LENGTH_SHORT).show();
}
break;
case R.id.LED4:
if (checked) {
HardControl.ledCtrl(4, 1);
Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
} else {
HardControl.ledCtrl(4, 0);
Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
}
break;
}
} /*onCreate是方法的入口,我们所有的操作都在这里面做*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); /*打开驱动设备节点*/
HardControl.ledOpen(); /*
* 光标定位到()中后,Ctrl+Shift+Space自动填充Button实现强制类型转换。
* 这里使用了findViewById(),在main_activity.xml中就需要给Button控件指定一个id.
* 双击BUTTON,按Ctrl+b会跳转到main_activity.xml文件中定义的地方
* 双击BUTTON,按Alt+Fn+F7可以打开其引用的地方。我这里没有跳出来R.java,但是老师的跳出来了。
* 光标定位在()中,Ctrl+Shift+Space键自动补上括号中的Button
*/
button = (Button) findViewById(R.id.BUTTON);
/* 找到这个Button并设置其监听器,此后按下Button后会触发上面的onClick()被调用 */
button.setOnClickListener(new MyButtonListener()); /* 光标定位在()中间,Ctrl+shift+Space会自动填充强制类型转换*/
checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
checkBoxLed4 = (CheckBox) findViewById(R.id.LED4); /*
// OnClickListener是View类的内部类,而且是一个接口,这里定义了一个匿名类,
// 看起来很别扭,改为上面的操作了。
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Code here executes on main thread after user presses button
ledon = !ledon;
if (ledon) {
button.setText("ALL OFF");
} else {
button.setText("ALL ON");
}
}
});
*/
}
}
3.HardControl.java
/* 参考MainActivity.java, HardControl.java存放在目录com/example/mm/hardLibrary下的,所以包名是这个 */
package com.example.mm.hardLibrary; public class HardControl {
/* 指定与驱动通过的JNI函数 */
public static native int ledCtrl(int which, int status);
public static native int ledOpen();
public static native void ledClose(); static {
try {
/*选中这一行,Ctrl+Alt+t自动补全catch异常的代码*/
System.loadLibrary("hardcontrol");
} catch (Exception e) {
e.printStackTrace();
}
}
}
三、JNI库实现
1.hardcontrol.c实现
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <jni.h>
#include <android/log.h> /*liblog*/ jint g_fd; /*可以先使用java -jni <class文件>来查看这些native函数的函数签名*/
jint led_open(JNIEnv *env, jclass cls) {
g_fd = open("/dev/leds", O_RDWR);
if (g_fd < ) {
/*Android liblog中的打印函数,它可以在AS和串口上打印出来,不能使用printf(),它无法打印出来*/
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "led_open failed!");
return -;
}
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "led_open called");
return ;
} jint led_ctrl(JNIEnv * env, jclass cls, jint which, jint status) {
jint ret;
/*这里status和which是反的,为了使用tiny4412_led.c这个驱动*/
ret = ioctl(g_fd, status, which);
if (ret != ) {
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "led_ctrl failed!: which=%d, status=%d", which, status);
return -;
}
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "led_ctrl called: which=%d, status=%d", which, status);
return ;
} void led_close(JNIEnv *env, jclass cls) {
close(g_fd);
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "led_close called!");
} #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static const JNINativeMethod methods[] = {
{"ledOpen", "()I", (void *)led_open},
{"ledCtrl", "(II)I", (void *)led_ctrl},
{"ledClose", "()V", (void *)led_close},
}; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
jclass cls; if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4)) {
return JNI_ERR; /* JNI version not supported */
} /*注意这里是包名(定义这些native函数的文件路径名)*/
cls = (*env)->FindClass(env, "com/example/mm/hardLibrary/HardControl");
if (cls == NULL) {
return JNI_ERR;
} if ((*env)->RegisterNatives(env, cls, methods, ARRAY_SIZE(methods)) < )
return JNI_ERR; return JNI_VERSION_1_4;
}
2.将编译出来的libhardcontrol.so放到apk包中的方法:
(1)编译.so文件的时候需要额外加上-nostdlib选项。
加这个选项的目录是要.so文件不要使用标准的libc.so.6库,而是使用我们指定的C库libc.so。因为tiny4412开发板上没有libc.so.6这个库文件,只有libc.so这个文件.
$ arm-linux-gcc -shared -fPIC hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /media/ubuntu/works/tiny4412/android-5.0.2/prebuilts/ndk/9/platforms/android-18/arch-arm/usr/lib/libc.so
(2)在app/libs目录下建立armeabi子目录,放入.so文件。
(3)修改Module:app的build.gradle,在顶层defaultConfig下加:
sourceSets {
main {
jniLibs.srcDirs = ['libs'] 表示so文件是放在这个libs目录下面
}
}
注意修改了gradle文件之后必须点击工具栏sync一下,否则libs文件夹根本不会编译静apk文件中,可以使用2345好压打开apk文件进行查看。
3.不能在hardcontrol.so文件中直接是使用printf(),printf()的打印我们是看不见的。需要使用Android提供的liblog库进行打印,它可以在串口终端也可以在AS的logcat窗口打印出来。
#include <android/log.h> /*liblog*/
__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "xxxxx");
如下可以编译成功:
$ arm-linux-gcc -shared -fPIC hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -I /media/ubuntu/works/tiny4412/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include -nostdlib /media/ubuntu/works/tiny4412/android-5.0.2/prebuilts/ndk/9/platforms/android-18/arch-arm/usr/lib/libc.so
但是执行时报错:找不到__android_log_print,所以还需要添加一个库:
在prebuilts目录下查找liblog.so,然后拷贝和头文件对应的loblog.so,链接的时候要链接到它,改为:
$ arm-linux-gcc -shared -fPIC hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -I /media/ubuntu/works/tiny4412/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include -nostdlib /media/ubuntu/works/tiny4412/android-5.0.2/prebuilts/ndk/9/platforms/android-18/arch-arm/usr/lib/libc.so /media/ubuntu/works/tiny4412/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so
====>使用arm-linux-gcc可以直接链接成功Android工程编译出来的库!
====>编译时加了-nostdlib,运行时再找不到符号,不一定就是开发板上没有这个库了,可能编译时根本就没有链接这个库。
4.驱动中的class_create()和device_create()的作用是在/sys目录下创建一些文件,然后udev机制根据这些文件创建设备节点。
四、驱动
驱动使用的是tiny4412_leds.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> #define DEVICE_NAME "leds" static int led_gpios[] = {
EXYNOS4212_GPM4(),
EXYNOS4212_GPM4(),
EXYNOS4212_GPM4(),
EXYNOS4212_GPM4(),
}; #define LED_NUM ARRAY_SIZE(led_gpios) /*要使用这个驱动就需要将cmd和arg的值在JNI层互换*/
static long tiny4412_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case :
case :
if (arg > LED_NUM) {
return -EINVAL;
} gpio_set_value(led_gpios[arg], !cmd);
//printk(DEVICE_NAME": %d %d\n", arg, cmd);
break; default:
return -EINVAL;
} return ;
} static struct file_operations tiny4412_led_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tiny4412_leds_ioctl, /*没有提供open和close指针也是可以使用的*/
}; static struct miscdevice tiny4412_led_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &tiny4412_led_dev_fops,
}; static int __init tiny4412_led_dev_init(void) {
int ret;
int i; for (i = ; i < LED_NUM; i++) {
ret = gpio_request(led_gpios[i], "LED");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
led_gpios[i], ret);
return ret;
} s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], );
} ret = misc_register(&tiny4412_led_dev); printk(DEVICE_NAME"\tinitialized\n"); /*启动时打印了这个,说明它注册的驱动*/ return ret;
} static void __exit tiny4412_led_dev_exit(void) {
int i; for (i = ; i < LED_NUM; i++) {
gpio_free(led_gpios[i]);
} misc_deregister(&tiny4412_led_dev); /*这个应该放在前面*/
} module_init(tiny4412_led_dev_init);
module_exit(tiny4412_led_dev_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
Android项目——触摸按键控制LED的更多相关文章
- Qt小项目之串口助手控制LED
Qt小项目之串口助手控制LED 前言 最近刚学了一点Qt开发上位机,尝试着做个小软件练练手.查找了很多资料,做了一个简单的串口助手,可以实现串口基本发送和接收功能,支持中文显示,还可以控制STM32开 ...
- 24-ESP8266 SDK开发基础入门篇--Android TCP客户端.控制 Wi-Fi输出PWM的占空比,调节LED亮度
https://www.cnblogs.com/yangfengwu/p/11204436.html 刚才有人说需要点鸡汤.... 我想想哈;我还没问关于哪方面的鸡汤呢!!! 我所一直走的路线 第一: ...
- 使用Roboguice依赖注入规划Android项目
前言 好久没写博客了,罪过啊-记事本里累积了不少东西,整理整理放上来. 关于依赖注入 Dependency Injection( 依赖注入)可以很好的帮助我们分离模块,降低耦合.提高可测试性.(PS: ...
- gradle构建android项目
工具: Android Studio2.0 gradle-2.10 一.Android常识 在做Android开发的时候我们首先必须要有一个SDK.一般SDK的主要作用就是将硬件和软件进行分离,做软件 ...
- Android项目svn代码管理问题[转]
用svn控制版本,svn本身是不会识别哪些该传,哪些不该传,这就导致有些关于路径的东西(比如拓展jar的路径)也被上传了,而当别人下载后,那个路径对于这个人可能完全不存在,项目编译就会出问题.用ecl ...
- 第一个Android项目——计算器
第一个Android项目——计算器 效果 开始学Android一两个星期了,学习了一下基本的Activity.简单控件及几个简单布局,打算找个东西来练练手,于是就选择发计算器.关于计算器中用到的四则运 ...
- Android项目中如何用好构建神器Gradle?(转)
最近在忙团队并行开发的事情,主要是将各个团队的代码分库,一方面可以降低耦合,为后面模块插件化做铺垫,另一方面采用二进制编译,可以加快编译速度.分库遇到了一些问题,很多都要通过Gradle脚本解决,所以 ...
- Android项目svn代码管理问题
用svn控制版本,svn本身是不会识别哪些该传,哪些不该传,这就导致有些关于路径的东西(比如拓展jar的路径)也被上传了,而当别人下载后,那个路径对于这个人可能完全不存在,项目编译就会出问题.用ecl ...
- Eclipse开发Android项目安装配置
在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入Android浪潮的朋友们,为了确保大家能顺利完成开发环境的搭建, ...
随机推荐
- yarn web ui 参数详解
我们经常使用yarn调度,但是我们是否对调度队列显示参数真正了解呢? 下面我们来一一看看这些参数都是做什么用的,代表什么意思 hadoop是通过队列管理集群资源,翻开集群Web UI,找到Sc ...
- django框架中form组件的简单使用示例:注册验证
Django中form组件的三大特点: 1. 生成页面可使用的HTML标签 2. 对用户提交的数据进行初步校验 3. 保留上次输入内容 废话不多说,直接进入正题. 这是注册界面截图: 与上一篇a ...
- Java抽象类总结规定
1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过.只有抽象类的非抽象子类可以创建对象. 2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类. 3. 抽 ...
- Maven中添加镜像
Maven库在天朝的下载速度实在是感人,所以添加镜像之后速度会提升很多. 在maven的settings.xml 文件里配置mirrors的子节点,添加如下mirror <mirror> ...
- 前端开发【第四篇: Dom操作】
文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式.我们最为关心的是,DOM把 ...
- 编写一个求和函数sum,使输入sum(2)(3)或输入sum(2,3),输出结果都为5
昨天的笔试题,做的一塌糊涂,题目考的都很基础而且很细,手写代码对我来说是硬伤啊.其中有一道是这个,然而看到题目的时候,根本没有想到arguments:然后现在就恶补一下. arguments:用在函数 ...
- soapui 测试 带hear 验证的写法
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web=& ...
- webservice和dubbo区别
webservice 不需要搭建注册中心,是一个服务框架,主要内容有soap协议,uuid注册中心,wsdl文件. dubbo 需要搭建注册中心,可以是zookeeper,redis.它是一个分 ...
- FUJI 富士 富仕 串口 N500I N700I 连接
本人丰富的硬件连接经验, 1.出售富士生化设备N500I, N700I 接入,C#代码demo, 可连接机器验证,验证后付款2. 提供宠物行业富士生化设备N500I, N700I型号接入软件技术支持 ...
- TkbmMWClientQuery的计算字段在CalcFields事件触发次数太多
kbmmw有两处BUG和作者提下,一个是TkbmMWClientQuery的计算字段在CalcFields事件触发次数太多,另一个是在TkbmMemTable的加载数据时字段会执行OnValidate ...