Tiny4412——Android访问硬件的方法

Android学习笔记--[1]硬件访问服务_基础知识 应用程序——》硬件访问服务——》JNI——》硬件对于不同的硬件,构建不同的硬件访问服务。 阅读详情

优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2019/03/15/Tiny4412——Android访问硬件的方法/#more

2019年,正式学习Android驱动。
以LED为切入点,学习Android访问硬件的方法。

之前写第一个Linux下的LED驱动时候,提到:Linux驱动 = 裸机 + 框架
这个思维在写Linux驱动的过程中,得到了充分验证。
如今,学习Android驱动,开始验证:Android驱动 = Linux驱动 + 框架

1. 环境搭建

开发环境主要包含:
1.一个Linux主机(虚拟机),在该主机里编译Linux内核和Android源码等;
2.一个Windows主机,在该主机里编写Android APP,并连接开发板调试;

1.1 搭建Ubuntu虚拟机

前面在学习Java时,已经安装了Ubuntu18.04的虚拟机,这里继续使用该虚拟机。

值得一提的是,虚拟机的内存给多一点,虚拟机参考配置:内存6G,硬盘100G,CPU 8核。
在编译内核源码的过程中,发现编译太慢,一般挂一晚上都能编译好,之前内存给小了,编译了一天一夜也没好,后来改大了内存后,一会就编译完了。

1.2 编译Kernel和Android

以下所有安装包都是从百问网云盘下载。

1、安装交叉编译工具链
下载arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz并解压。
修改/etc/environment,将解压的交叉编译工具链加在末尾:

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/work/tools/toolschain/4.5.1/bin"

执行source /etc/environment更新环境变量。
此时执行arm-linux-gcc -v,打印出gcc version 4.5.1 (ctng-1.8.1-FA)即OK。

2、编译Kernel
下载linux-3.0.86-20150324.tgz并解压。
进入内核源码根目录执行:

cp tiny4412_android_defconfig .config
make zImage

最后生成/work/linux_source/linux-3.0.86/arch/arm/boot/zImage。

3、编译Android
下载android-5.0.2-fs-20150325.tar.gz并解压。
进入Android源码根目录执行:

. setenv
lunch

在选择界面:

You're building on Linux

Lunch menu... pick a combo:
     1. aosp_arm-eng
     2. aosp_arm64-eng
     3. aosp_mips-eng
     4. aosp_mips64-eng
     5. aosp_x86-eng
     6. aosp_x86_64-eng
     7. mini_emulator_x86-userdebug
     8. m_e_arm-userdebug
     9. mini_emulator_arm64-userdebug
     10. mini_emulator_mips-userdebug
     11. mini_emulator_x86_64-userdebug
     12. aosp_hammerhead-userdebug
     13. aosp_mako-userdebug
     14. full_tiny4412-userdebug
     15. full_tiny4412-eng
     16. aosp_shamu-userdebug
     17. aosp_deb-userdebug
     18. aosp_flo-userdebug
     19. aosp_grouper-userdebug
     20. full_fugu-userdebug
     21. aosp_fugu-userdebug
     22. aosp_tilapia-userdebug
     23. aosp_manta-userdebug

Which would you like? [aosp_arm-eng] 

输入15,即选择full_tiny4412-eng。
再执行make –j8,等待几个小时,编译完成后执行./gen-img.sh生成/work/android_source/android-5.0.2/system.img.

当然编译过程不可能是那么一帆风顺的,实际编译Android源码过程中遇到的问题有:
①JDK版本过高,重新安装openjdk-7-jdk,参考解决链接。
②编译报错aidl_language_l,参考解决链接。
③编译报错error: unsupported reloc 43,参考解决链接。
④制作映像文件出错mkimage: command not found,执行sudo apt-get install u-boot-tools安装即可。

4、烧写
使用厂家提供的MiniTools。
将zImage和system.img分别烧写到Android Kernel和Android RooFs/System Image。

1.3 安装JDK(Windows)

因为要在Windows上使用Android Studio编写APP,所以在Windows上也需要安装Java JDK(Java Development Kit)。
通过Oracle官网下载最新的JDK:

安装好后,将JDK安装路径加入Windows的环境变量:

最后在CMD里验证:

1.4 安装Android Studio

首先从官网下载Android Studio。

点击安装,会弹出首次运行没有Android SDK,点击“Cancel”。

然后选择"Custom"以自定义SDK安装路径。在弹出的界面勾选上"Android Virtual Device",再指定SDK的安装路径。

此时会下载Android SDK和AVD,两个文件都很大,为了C盘节省空间,应用界面只指定了SDK的安装路径在D盘,还需手动指定AVD的安装路径,这个需要修改环境变量设置。

然后,等待一段时间就安装完了。

1.5 Android Studio的常用快捷键

参考博客:Android Studio2.0 教程从入门到精通MAC版 - 提高篇

2. Android应用程序

我的目的是写一个Android应用程序,通过该应用程序控制开发板上的LED灯。
因此首先得设计一个APP,该APP有一个总开关控制所有灯的亮灭,每个灯又有单独的开关控制亮灭。

2.1 创建APP工程

打开Android Studio,选择"Start a new Android Studio project"创建一个新APP工程。
然后选择"Empty Activity"空主界面,最后设置APP信息、保存路径、兼容API版本。

等待自动创建完成后,会自动生成工程文件,其中有两个文件后面常用:

  • app\src\main\res\layout\activity_main.xml,界面控件布局文件,既可通过图形界面设计控件,也可直接编辑xml;
  • app\src\main\java\cn\hceng\led\MainActivity.java,实现控件各种具体功能,逻辑关系。

2.2 常用控件介绍

2.2.1 TextView

TextView是文本视图,用来显示文字的。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.标签<TextView />代表着要在Activity中添加一个TextView,标签中可以设置一些属性;
2.**android:id代表TextView的ID,也就是TextView的唯一标识;在java代码中我们可以通过findViewById()方法来通过ID获取控件;上述控件的唯一ID为TEXT;
3.
android:layout_width代表控件的宽度,该属性的值是match_parent,表示该控件的宽度与父视图的宽度相同;
4.
android:layout_height代表控件的高度,该属性的值是wrap_content,表示控件的高度根据内容的高度进行改变;
5.
android:gravity代表文字对齐方式,该属性的值是center,表示居中显示;
6.
android:text代表文字内容,该属性的值是hceng first APP!,表示显示这串字符;
7.
android:textColor代表文字的颜色,该属性的值是#008577,表示16进制色值的绿色;
7.
android:textSize代表文字的大小,该属性的值是25sp,长度宽度的数值用dp,字体的大小用sp;
7.
android:textStyle**代表文字的样,normal、bold、italic分别为正常,加粗、斜体,默认为normal;

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.TextView;

      TextView myTextView = (TextView) findViewById(R.id.TEXT);
      String myText = myTextView.getText().toString();
      myTextView.setText(myText+"  Add String");
    

{% endcodeblock %}

2.2.2 Button

Button是按钮,用来控制某个目的的开关。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.Button标签里面的属性和前面TextView基本类似,这里就不赘述了;

  • 代码控制:
    在Activity的类中也是使用findViewById()来通过ID获取该按钮。
    获取按钮后需要给按钮绑定点击事件,也就是点击按钮要做的事情,常用的方法有四种:布局文件指定响应函数、直接匿名内部类、新建子类实现、在当前类实现接口。

布局文件指定响应函数:

在布局文件的<Button />标签里,添加android:onClick="onClick",MainActivity.java里就只需要实现onClick()方法即可。

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);
}

public void onClick(View v) {
    //Todo
}

}
{% endcodeblock %}

直接匿名内部类:

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);

    button.setOnClickListener(new View.OnClickListener() {
        public void onClick (View v){
            //Todo
        }
    });
}

}
{% endcodeblock %}

新建子类实现:

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);
    button.setOnClickListener(new MyButtonListener());
}

class MyButtonListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
        //Todo
    }
}

}
{% endcodeblock %}

在当前类实现接口:

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;
import android.view.View.OnClickListener;

public class MainActivity extends AppCompatActivity implements OnClickListener { //注意这里实现接口

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);
    button.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    //Todo
}

}
{% endcodeblock %}

参考博客:Android代码规范----按钮单击事件的四种写法

2.2.3 CheckBox

CheckBox是复选框,用来多项选择。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

  • 分析:

1.CheckBox标签里面的属性和前面也基本类似;
2.android:onClick代表控件的响应函数,该属性的值是onCheckboxClicked,表示操作该控件将调用onCheckboxClicked()方法;

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.CheckBox;
    import android.widget.Toast;

    CheckBox checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);

    public void onCheckboxClicked(View v) {
    boolean checked = ((CheckBox) v).isChecked();

      switch (v.getId()) {
          case R.id.LED1:
              if (checked) {
                  Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
              } else {
                  Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
              }
              break;
          case R.id.LED2:
              //Todo
              break;
      }
    

    }
    {% endcodeblock %}

在XML里,多个CheckBox的android:onClick都是onCheckboxClicked,也就是任意一个CheckBox被点击,都会调用到onCheckboxClicked()方法。
因此,需要使用getId()获取是按的哪一个CheckBox,再根据isChecked()的状态,进行相应操作。

2.2.4 EditText

EditText是输入框,用来接收用户输入的数据。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.**android:hint代表占提示字符串,用来提示用户输入内容;
2.
android:macLines**表示显示的最多行数;

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.EditText;
    import android.widget.Toast;

      EditText myEditText = (EditText) findViewById(R.id.EDIT_TEXT);
      String inputText = myEditText.getText().toString();
      Toast.makeText(MainActivity.this, inputText, Toast.LENGTH_SHORT).show();
    

{% endcodeblock %}

一般EditText还需要监控键盘回车键,或者和Button搭配,从而判断用户输完了数据,再获取数据。

2.2.5 ImageView

ImageView是图片显示框,用来在界面上显示贴图。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.**android:src**代表图片资源的,使用@xx来引用,图片一般放在res/drawable里面;

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.ImageView;

      ImageView imageView = (ImageView) findViewById(R.id.IMAGEVIEW);
      imageView.setImageResource(R.drawable.p2);
    

{% endcodeblock %}

2.2.6 ProgressBar

ProgressBar是进度条,用来展示当前进度。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.**style用来指定进度条样式,默认是转动的圆,这里是水平的一根横线表示进度条;
2.
android:max表示进度条的最大值;
3.
android:visibility表示是否可见进度条,visible是可见,invisible是不可见,gone是隐藏,即不保留控件所占空间;
4.
android:progress**表示进度条的当前值(初始值);

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.ProgressBar;

      ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.PROGRESSBAR);
      myProgressBar.setProgress(myProgressBar.getProgress()+10);
    
      if (myProgressBar.getProgress() == myProgressBar.getMax()) {
          //myProgressBar.setVisibility(View.GONE);
          myProgressBar.setProgress(0);
      } else {
          myProgressBar.setVisibility(View.VISIBLE);
      }
    

{% endcodeblock %}

1、通过findViewById()方法找到指定ProgressBar;
2、通过getProgress()方法获取当前进度值,setProgress()方法设置当前进度值;
3、通过getMax()方法获取xml设置的最大值;
4、通过setVisibility()方法设置进度条是否可见,VISIBLE、INVISIBLE、GONE含义同上;

2.2.7 AlterDialog

AlterDialog是警告对话框,用来展示警告信息和简单的交互。

  • 代码创建:
    {% codeblock lang:java %}
    import android.widget.Toast;
    import android.app.AlertDialog;
    import android.content.DialogInterface;

      AlertDialog.Builder alterDialog = new AlertDialog.Builder(MainActivity.this);
      alterDialog.setTitle("Warning");
      alterDialog.setMessage("Warning content");
      alterDialog.setCancelable(false);
      alterDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
              Toast.makeText(MainActivity.this, "yes", Toast.LENGTH_SHORT).show();
          }
      });
    
      alterDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
              Toast.makeText(MainActivity.this, "no", Toast.LENGTH_SHORT).show();
          }
      });
    
      alterDialog.show();
    

{% endcodeblock %}

1、AlterDialog是通过Builder进行创建,在创建的时候会指定该AlterDialog在那个Activity上进行显示;
2、通过setTitle()方法设置标题,通过setMessage()设置内容;
3、setCancelable()方法,表示弹出的AlterDialog在用户点击返回键是否消失,默认是true,这里设置为false,表示点击返回键不消失;
4、setPositiveButton()方法是设置点击“确定”按钮时的事件,setNegativeButton是设置点击“取消”按钮的事件;
5、show()方法弹出AlterDialog;

2.2.8 ProgressDialog

ProgressDialog是进度提示框,即在AlterDialog上添加Progress。

  • 代码创建:
    {% codeblock lang:java %}
    import android.app.ProgressDialog;

      ProgressDialog myProgressDialog = new ProgressDialog(MainActivity.this);
    
      myProgressDialog.setTitle("ProgressDialog");
      myProgressDialog.setMessage("Loading……");
      myProgressDialog.setCancelable(true);
    
      myProgressDialog.show();
    

{% endcodeblock %}

2.3 四种布局介绍

Android中的布局方式有四种:线性布局(LinearLayout)、相对布局(RelativeLayout)、帧布局(FrameLayout)、表格布局(TableLayout)。

2.3.1 线性布局

线性布局是最常用的布局之一,如名字一样,将控件在线性方向上依次排列。
既然是线性,那么就可能是水平线性horizontal,也可以是垂直线性vertical。

  • xml示例:
    {% codeblock lang:xml %}
    <LinearLayout
    android:layout_width=“match_parent”
    android:layout_height=“match_parent”
    android:orientation=“vertical” >

      <LinearLayout <!--LinearLayout 1.1-->
          android:layout_width="match_parent"
          android:layout_height="960px"
          android:background="#005A5B" >
    
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="A"
              android:textSize="100dp"/>
      </LinearLayout>
    
      <LinearLayout <!--LinearLayout 1.2-->
          android:layout_width="match_parent"
          android:layout_height="960px"
          android:orientation="horizontal" >
    
          <LinearLayout <!--LinearLayout 1.2.1-->
              android:layout_width="540px"
              android:layout_height="match_parent"
              android:background="#008C72" >
    
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="B"
                  android:textSize="100dp"/>
          </LinearLayout>
    
          <LinearLayout <!--LinearLayout 1.2.2-->
              android:layout_width="540px"
              android:layout_height="match_parent"
              android:background="#02A676" >
    
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="C"
                  android:textSize="100dp"/>
          </LinearLayout>
      </LinearLayout>
    

{% endcodeblock %}

  • 效果:

示例实现了三个色块,最外层的LinearLayout 1是整个区域,它指定内部按垂直排列。
然后里面的两个LinearLayout 1.1和LinearLayout 1.2就按垂直的上下排列。
再在LinearLayout 1.2里又指定水平排列,里面的LinearLayout 1.2.1和LinearLayout 1.2.2就左右排列。

2.3.2 相对布局

相对布局可以根据已经固定的控件来确定其它新加控件的位置。

  • xml示例:
    {% codeblock lang:xml %}
<?xml version="1.0" encoding="utf-8"?>

<Button
    android:id="@+id/button_center"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="center" />

<Button
    android:id="@+id/button_above"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@+id/button_center"
    android:layout_centerInParent="true"
    android:text="above" />

<Button
    android:id="@+id/button_below"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/button_center"
    android:layout_centerInParent="true"
    android:text="below" />

<Button
    android:id="@+id/button_left"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_toLeftOf="@+id/button_center"
    android:text="left" />

<Button
    android:id="@+id/button_right"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_toRightOf="@+id/button_center"
    android:text="right" />
{% endcodeblock %}
  • 效果:

示例实现了五个按键,先确定最中间button_center。
然后使用layout_above、layout_below、layout_toLeftOf、layout_toRightOf分别在其上下左右。
其中layout_centerVertical作用是将控件置于父控件的中心位置。

2.3.3 帧布局

帧布局就是以屏幕左上角为坐标原点,指定每个控件的大小,后加进来的控件覆盖前面的控件。

  • xml示例:
    {% codeblock lang:xml %}






    {% endcodeblock %}

  • 效果:

示例实现了五个色块,都以左上角为原点,设置不同的大小,实现层级覆盖的效果。

2.3.4 表格布局

在表格布局中,整个页面就相当于一张大的表格,控件就放在每个表格中。

  • xml示例:
    {% codeblock lang:xml %}

      <TableRow>
          <LinearLayout
              android:layout_width="75pt"
              android:layout_height="75pt"
              android:background="#00BF80">
          </LinearLayout>
    
          <LinearLayout
              android:layout_width="75pt"
              android:layout_height="75pt"
              android:background="#298A6A">
          </LinearLayout>
      </TableRow>
    
      <TableRow>
          <LinearLayout
              android:layout_width="75pt"
              android:layout_height="75pt"
              android:background="#00734D"
              android:layout_span="2">
          </LinearLayout>
      </TableRow>
    

{% endcodeblock %}

  • 效果:

示例实现了三个色块,先用TableLayout圈出整个表格,stretchColumns表示第N列自动扩充,可以看到上面的两个色块虽然设置的大小一样,但第0列的比第1列的大,就是因为第0列的自动扩充占据了两个剩余的部分。
TableRow代表一行,在标签里可以根据需求设置多个控件,
再用TableRow,表示继续下一行,layout_span属性表示该控件占两个表格。

参考博客:Android开发之基本控件和详解四种布局方式

2.4 设计的APP

有了上面的基础知识,基本就可以编写APP了。

2.4.1 布局设计

布局比较简单,采用线性布局,里面有6个控件:1个TextView显示字符、1个Button作为所有LED总开关、4个CheckBox分别控制每一个LED。
{% codeblock lang:xml %}

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout 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”
tools:context=".MainActivity">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/TEXT"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_marginTop="30dp"
        android:text="Android access hardware"
        android:textColor="#008577"
        android:textSize="25sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/BUTTON"
        android:layout_width="50pt"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_gravity="center"
        android:text="ALL ON" />

    <CheckBox
        android:id="@+id/LED1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onCheckboxClicked"
        android:text="LED1" />

    <CheckBox
        android:id="@+id/LED2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onCheckboxClicked"
        android:text="LED2" />

    <CheckBox
        android:id="@+id/LED3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onCheckboxClicked"
        android:text="LED3" />

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

</LinearLayout>

</android.support.constraint.ConstraintLayout>
{% endcodeblock %}

2.4.2 代码操作

代码的操作逻辑也很简单,先是获取每个按键的ID,然后绑定button按键监听和chekbox按键监听。
在监听的函数里,进行相应的逻辑控制。这里展示屏蔽了所有的硬件操作,硬件操作在后面再添加。

{% codeblock lang:java %}
package cn.hceng.led;

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;

public class MainActivity extends AppCompatActivity {

private boolean ledStatus = false;

private Button button = null;
private CheckBox checkBoxLed1 = null;
private CheckBox checkBoxLed2 = null;
private CheckBox checkBoxLed3 = null;
private CheckBox checkBoxLed4 = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initView();
}

private void initView() {
    checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
    checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
    checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
    checkBoxLed4 = (CheckBox) findViewById(R.id.LED4);

    button = (Button) findViewById(R.id.BUTTON);
    button.setOnClickListener(new MyButtonListener());
}

class MyButtonListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
        ledStatus = !ledStatus;

        if (ledStatus) {
            button.setText("ALL OFF");
            checkBoxLed1.setChecked(true);
            checkBoxLed2.setChecked(true);
            checkBoxLed3.setChecked(true);
            checkBoxLed4.setChecked(true);
        } else {
            button.setText("ALL  ON");
            checkBoxLed1.setChecked(false);
            checkBoxLed2.setChecked(false);
            checkBoxLed3.setChecked(false);
            checkBoxLed4.setChecked(false);
        }
    }
}

public void onCheckboxClicked(View view) {
    boolean checked = ((CheckBox) view).isChecked();

    switch (view.getId()) {
        case R.id.LED1:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
            }
            break;
        case R.id.LED2:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
            }
            break;

        case R.id.LED3:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED3 off", Toast.LENGTH_SHORT).show();
            }
            break;

        case R.id.LED4:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
            }
            break;
        default:
            break;
    }
}

}
{% endcodeblock %}

3. Android访问硬件的方法

Android APP采用Java编写的,Java不能直接访问硬件,因此Android APP访问硬件只能两种方式:通过JNI直接访问和Android硬件访问服务。

3.1 通过JNI直接访问

驱动层:
和Linux驱动完全一样,编写驱动,向上提供/dev/leds节点;
C库:
使用C/C++操作/dev/leds,将C库函数名和Java函数名建立映射关系,注册java本地方法;
应用层:
APP加载C库(so文件),加载后便可以调用C库提供的java本地方法:open()、ioctl()、close()实现对硬件的操作;

3.1.1 编写LED驱动

LED的驱动很简单,就不多废话了。

{% codeblock lang:c %}
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/device.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>

static int led_gpios[] = {
EXYNOS4212_GPM4(0),
EXYNOS4212_GPM4(1),
EXYNOS4212_GPM4(2),
EXYNOS4212_GPM4(3),
};

static int led_open(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < 4; i++)
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);

return 0;

}

/* app : ioctl(fd, cmd, arg) */
static long led_ioctl(struct file filp, unsigned int cmd, unsigned long arg)
{
/
cmd : 0-off, 1-on /
/
arg : 0-3, which led */

if ((cmd != 0) && (cmd != 1))
    return -EINVAL;

if (arg > 4)
    return -EINVAL;

gpio_set_value(led_gpios[arg], !cmd);

return 0;

}

static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = led_ioctl,

};

static int major;
static struct class *cls;

int leds_init(void)
{
major = register_chrdev(0, “leds”, &leds_ops);

cls = class_create(THIS_MODULE, "leds");
device_create(cls, NULL, MKDEV(major, 0), NULL, "leds"); /* /dev/leds */

return 0;

}

void leds_exit(void)
{
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, “leds”);
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng huangcheng.job@foxmail.com”);
MODULE_DESCRIPTION(“Tiny4412 leds driver.”);
MODULE_VERSION(“v1.0”);
{% endcodeblock %}

编写好后,放在内核目录drivers/char/里,并在Makefile里添加obj-y += leds_drv.o。
重新编译make zImage,将内核重新烧写到开发板里。

3.1.2 编写C库文件

参考前面Java的JNI,编写hardcontrol.c,生成libhardcontrol.so。
在hardcontrol.c里,操作/dev/leds,将操作函数与Java方法绑定,并注册。

{% codeblock lang:c %}
#include <stdio.h>
#include <jni.h>

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

#include <android/log.h>

#define DEBUG 1

#if(DEBUG==1)
#define LOG_TAG “JNI”
#define LOGV(…) __android_log_print(ANDROID_LOG_VERBOSE,TAG,VA_ARGS)
#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, VA_ARGS)
#define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, VA_ARGS)
#define LOGE(…) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, VA_ARGS)
#else
#define LOGV(…) NULL
#define LOGD(…) NULL
#define LOGI(…) NULL
#define LOGE(…) NULL
#endif

static jint fd;

jint ledOpen(JNIEnv *env, jobject cls)
{
LOGD(“hardcontrol ledOpen\n”);

fd = open("/dev/leds", O_RDWR);
if (fd >= 0)
    return 0;
else
    return -1;

}

void ledClose(JNIEnv *env, jobject cls)
{
LOGD(“hardcontrol ledClose\n”);

close(fd);

return;

}

jint ledCtrl(JNIEnv *env, jobject cls, jint number, jint status)
{
LOGD(“hardcontrol ledCtrl number=%d status=%d\n”, number, status);

int ret = ioctl(fd, status, number);

return ret;

}

static const JNINativeMethod methods[] = {
{“ledOpen”, “()I”, (void *)ledOpen},
{“ledClose”, “()V”, (void *)ledClose},
{“ledCtrl”, “(II)I”, (void *)ledCtrl},
};

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls;

if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) 
    return JNI_ERR; 

cls = (*env)->FindClass(env, "cn/hceng/hardlibrary/HardControl"); 
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;

}
{% endcodeblock %}

编译:

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so

这里的-I指定头文件jni.h的路径;-nostdlib是为了不使用标准的libc库,因为后面libhardcontrol.so依赖libc.so.6,需要重新构建系统得到libc.so.6,但源码中有很多libc.so也能用,为了方便找了一个版本较高的libc.so。

最后把生成的libhardcontrol.so放在APP的D:\Android\APP\LED\app\libs\armeabi\路径下。

如何在C语言中打印log到android控制台:
1.修改app\build.gradle,在Android里添加ndk:

        //hceng add for c language printing
        ndk {
            moduleName "printing"
            abiFilters "armeabi","armeabi-v7a","x86"

            ldLibs "log"
        }

2.C文件中添加:

#include <android/log.h>
#define DEBUG 1 

#if(DEBUG==1)
#define LOG_TAG "JNI"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#else
#define LOGV(...) NULL
#define LOGD(...) NULL
#define LOGI(...) NULL
#define LOGE(...) NULL
#endif

3.和printf一样使用:

  //打印logcat
  LOGD("name=%s age=%f\n",name,age);

4.编译C文件,需指定android/log.h路径和添加liblog.so库:

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -I /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/ -nostdlib /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so

参考博客:C语言中打印log到android控制台

3.1.2 修改APP文件

在APP目录app\src\main\java\cn\hceng\hardlibrary\下创建HardControl.java来加载C库和声明Native方法:
{% codeblock lang:java %}
package cn.hceng.hardlibrary;

public class HardControl {
//1.load
static {
try {
System.loadLibrary(“hardcontrol”); //Call libhardcontrol.so from C.
} catch (Exception e) {
e.printStackTrace();
}
}
//2.Function declaration
public static native int ledCtrl(int number, int status);
public static native int ledOpen();
public static native void ledClose();
}
{% endcodeblock %}

在APP里,即可通过JNI调用本地方法:
{% codeblock lang:java %}
import cn.hceng.hardlibrary.*; //硬件库

HardControl.ledOpen(); //硬件初始化
HardControl.ledCtrl(0, 1); //硬件操作
HardControl.ledCtrl(0, 0); //硬件操作

{% endcodeblock %}
在APP的适当位置,根据操作逻辑,调用本地方法。

最后还要修改app\build.gradle,在Android里添加:

    //hceng add for led
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs'];
        }
    }

此时修改后,将不能在AVD里仿真了,必须在开发板上测试。

最后,编译APP,插上USB线,连接开发板,运行测试,通过APP实现对LED的控制。

3.2 Android硬件访问服务

前面直接通过JNI访问硬件,操作比较简单,同时也存在一些问题。
比如,当多个APP同时访问某个硬件时,就可能发生硬件冲突。
为了解决这个问题,Android提供了硬件访问服务,APP不能直接访问硬件,而是都给System Server申请操作硬件,System Server决策后,再去访问硬件,解决多个APP同时访问硬件冲突的问题。

驱动层:
和Linux驱动完全一样,编写驱动,向上提供/dev/leds节点;
C库:
HAL层操作/dev/leds,JNI层加载HAL文件和向上注册java本地方法;
系统服务:
通过注册android服务的方式加载C库,再将服务加入service_manager里面。
应用层:
从service_manager里获取相关服务,再通过接口调用,接口里实现对本地方法的调用。

3.2.1 编写AIDL

AIDL(Android Interface Definition Language),即Android接口定义语言,顾名思义就是定义接口,提供给APP使用。
LED对APP而言,就一个控制方法,因此ILedService.aidl如下:
{% codeblock lang:aidl %}
package android.os;

interface ILedService {
int ledCtrl(int number, int status);
}
{% endcodeblock %}

ILedService.aidl并不能直接被APP调用,需要生成ILedService.java才能被APP使用。
将ILedService.aidl放在frameworks/base/core/java/android/os/里,修改frameworks/base/Android.mk,添加:

core/java/android/os/ILedService.aidl \

然后执行mmm frameworks/base/,将自动生成out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java。

3.2.2 编写LedService.java

前面生成了ILedService.java,需要实现ILedService接口的成员函数。
{% codeblock lang:java %}
package com.android.server;
import android.os.ILedService;

public class LedService extends ILedService.Stub {
private static final String TAG = “ILedService”;

//Call native c function to access hardware
public int ledCtrl(int number, int status) throws android.os.RemoteException 
{
	return native_ledCtrl(number, status);
}

public LedService() {
	native_ledOpen();
}

//Function declaration
public static native int native_ledCtrl(int number, int status);
public static native int native_ledOpen();
public static native void native_ledClose();

}
{% endcodeblock %}

将其放在frameworks/base/services/core/java/com/android/server/路径下。

可以看到LedService.java继承于ILedService,并调用本地方法实现了成员函数。并在构造函数里调用native_ledOpen()。

3.2.3 修改SystemServer.java

SystemServer.java主要做两件事,一是加载C库,二是使用addService将LED服务加入service_manager里面。
加载C库这个是调用onload.cpp,这个后面只修改onload.cpp,这里就不需要改了。
修改frameworks/base/services/java/com/android/server/SystemServer.java,添加:
{% codeblock lang:java %}
Slog.i(TAG, “Led Service”);
ServiceManager.addService(“led”, new LedService());
{% endcodeblock %}

3.2.4 修改onload.cpp

SystemServer.java会加载C库,调用到onload.cpp,需要在onload.cpp注册LED服务。
修改frameworks/base/services/core/jni/onload.cpp,添加:
{% codeblock lang:c %}
int register_android_server_LedService(JNIEnv* env);

……

register_android_server_LedService(env);
{% endcodeblock %}

3.2.5 编写com_android_server_LedService.cpp

前面用到了register_android_server_LedService(),是在com_android_server_LedService.cpp里实现的。
com_android_server_LedService.cpp理论上可以直接操作节点/dev/leds,但一般不这样做。
通常的做法是,向上提供本地方法(native_ledOpen),向下加载hal文件(led_hal.c),并调用HAL的函数。
这样操作有两个好处:
1.方便修改;
如果需要修改硬件部分的操作,只需要修改led_hal.c,生成so文件,放入系统即可,而不需要编译整个Android系统;
2.保密代码;
因为Linux的GPL协议,一旦使用的内核代码,自己的代码也得开源出去,硬件厂商为了保密硬件的具体细节,只在内核实现操作寄存器的接口,具体的操作逻辑放在HAL文件里,而Android采用Apache协议,修改了代码而无需开源,这样就实现了保密代码;

Google的这一"骚"操作就不评论了,积极的看确实加快的Linux的传播,但也伤害了Linux的开源社区。

编写步骤如下:
1、定义JNINativeMethod,建立Java本地方法与C库函数名的映射关系;
2、使用jniRegisterNativeMethods注册本地方法,将在onload.cpp被调用;
3、在open()里:
 3.1、使用hw_get_module获得hw_module_t结构体;
 3.2、使用module->methods->open获得hw_device_t结构体;
 3.3、将hw_device_t转换为led_device_t,调用对应open;
4、完成其它函数ctrl、close的调用;

{% codeblock lang:cpp %}
#define LOG_TAG “LedService”

#include “jni.h”
#include “JNIHelp.h”
#include “android_runtime/AndroidRuntime.h”

#include <utils/misc.h>
#include <utils/Log.h>

#include <stdio.h>

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

#include <hardware/led_hal.h>

namespace android
{
static led_device_t* led_device;

jint ledOpen(JNIEnv *env, jobject cls)
{
    jint err;
    hw_module_t* module;
    hw_device_t* device;

    ALOGI("native ledOpen");

    //1. hw_get_module for get module
    err = hw_get_module("led", (hw_module_t const**)&module);
    if (err == 0) {
        //2. module->methods->open for get device 
        err = module->methods->open(module, NULL, &device);
        
        if (err == 0) {
            //3. conversion, call led_open
            led_device = (led_device_t *)device;
            return led_device->led_open(led_device);
        } else {
            return -1;
        }
    }
    return -1;
}

void ledClose(JNIEnv *env, jobject cls)
{
    ALOGI("nativeled Close");
    
    return;
}

jint ledCtrl(JNIEnv *env, jobject cls, jint number, jint status)
{
    ALOGI("native ledCtrl %d, %d", number, status);
    
    return led_device->led_ctrl(led_device, number, status);
}

static const JNINativeMethod method_table[] = {
    {"native_ledOpen",  "()I",   (void *)ledOpen}, 
    {"native_ledClose", "()V",   (void *)ledClose}, 
    {"native_ledCtrl",  "(II)I", (void *)ledCtrl}, 
};

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            method_table, NELEM(method_table));
}

};
{% endcodeblock %}

将其放到./frameworks/base/services/core/jni/路径下。

3.2.6 编写HAL文件led_hal.c

接着前面,HAL负责访问驱动程序执行硬件操作。

编写步骤如下:
1、创建一个名为HMI(HAL_MODULE_INFO_SYM)的hw_module_t结构体;
2、创建一个open函数, 它返回led_device_t结构体;
3、创建led_device_t结构体,里面包含hw_device_t结构体和提供的函数;
4、操作设备节点实现提供的函数;

{% codeblock lang:h [led_hal.h] %}
#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H

#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>

#include <hardware/hardware.h>

__BEGIN_DECLS

struct led_device_t {
struct hw_device_t common;

int (*led_open) (struct led_device_t* dev);
int (*led_ctrl) (struct led_device_t* dev, int number, int status);

};

__END_DECLS

#endif
{% endcodeblock %}

{% codeblock lang:c [led_hal.c] %}
#include <hardware/hardware.h>

#include <cutils/log.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>

#include <hardware/led_hal.h>

#define LOG_TAG “LED_HAL”

static int fd;

static int led_open(struct led_device_t* dev __unused)
{
fd = open("/dev/leds", O_RDWR);

ALOGI("led_open : %d", fd);

if (fd >= 0)
    return 0;
else
    return -1;

}

static int led_ctrl(struct led_device_t* dev __unused, int number, int status)
{
int ret = ioctl(fd, status, number);

ALOGI("led_ctrl : %d, %d, %d", number, status, ret);

return ret;

}

static int led_close(struct hw_device_t* device __unused)
{
ALOGI(“led_close : %d”, fd);

close(fd);

return 0;

}

static struct led_device_t led_dev = {
.common = {
.tag = HARDWARE_DEVICE_TAG,
.close = led_close,
},
.led_open = led_open,
.led_ctrl = led_ctrl,
};

static int led_device_open(const struct hw_module_t* module __unused, const char* id __unused,
struct hw_device_t** device)
{
//return by id
*device = &led_dev;

return 0;

}

static struct hw_module_methods_t led_module_methods = {
.open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.id = “led”,
.methods = &led_module_methods,
};
{% endcodeblock %}

将led_hal.h放在./hardware/libhardware/include/hardware/目录下;
将led_hal.c上放在./hardware/libhardware/modules/led/目录下;

3.2.7 编译和重新烧写system.img

前面添加了LedService.java,其Android.mk自动包含了所有java文件,不需要修改Android.mk。
前面添加了com_android_server_LedService.cpp,不会自动包含cpp文件,其Android.mk需要修改。
打开frameworks/base/services/core/jni/Android.mk,添加:

$(LOCAL_REL_DIR)/com_android_server_LedService.cpp \

前面添加了led_hal.c,需要创建hardware/libhardware/modules/led/Android.mk,内容如下:
{% codeblock lang:mk %}
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := led.default

LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng

include $(BUILD_SHARED_LIBRARY)
{% endcodeblock %}

然后执行:

mmm ./frameworks/base/services/             //保证com_android_server_LedService.cpp和onload.cpp都重新编译
mmm ./hardware/libhardware/modules/led/     //保证led_hal.c编译
make snod                                   //重新生成./out/target/product/tiny4412/system.img
./gen-img.sh                                //脚本,生成system.img并移动到源码根目录,且生成其他用户数据镜像等

将生成的system.img重新烧写,重新启动后界面卡在Android logo,串口一直打印request_suspend_state: wakeup (0->0)。
解决方法:
修改android源码的./device/friendly-arm/tiny4412/conf/init.rc
将mount rootfs rootfs / ro remount改为mount rootfs rootfs / rw remount,重新编译,烧写即可。

3.2.8 修改APP文件

在APP里,会用到ILedService类,这个类是我们自己定义的,直接在APP里使用,连编译都过不了。
因此,就需要把包含ILedService类的文件导入工程,或者使用JAVA的反射机制。

1、使用导入的方法
①首先还是修改MainActivity.java,修改其中对硬件的调用,参考如下:
{% codeblock lang:c %}
import android.os.ILedService;
import android.os.ServiceManager;
import static android.os.ServiceManager.getService;
……
ILedService iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));
……
iLedService.ledCtrl(0, 1);
{% endcodeblock %}
注意getService()的名字和SystemServer.java里addServer添加的名字要一致。

②将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar放在Windows里。
在Android Studio里,选择File->Project Structure,此时弹出配置界面,点击左上角的+,选择Import .JAR/.AAR Package,选择刚才的classes.jar。
再选中app,切换到Dependencies选项卡,点击右边的+号,选择3 Module dependency,在弹出的界面选择刚才添加的classes,最后点击OK。

以上操作后,会在APP工程根目录下生成`classes`文件夹,里面就包含了`classes.jar`。

为了减小生成的APP体积,还需要设置JAR包只在编译时使用,而不打包到APP里面。如下图,选中classes,在右边的选项卡选择Compile only。

③此时,再次编译可能遇到INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113等错误,在app\build.gradle添加:

    //hceng add for solved:INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113.
    splits {
        abi {
            enable true
            reset()
            include 'x86', 'armeabi-v7a', 'x86_64'
            universalApk true
        }
    }

2、使用反射机制

JAVA的反射机制原理参考之前的JAVA学习笔记——4.2反射。

使用放射机制就不用导入classes.jar,通过Class.forName()获取类、getMethod()获取方法,最终得到ledCtrl()方法。
{% codeblock lang:java %}
Object proxy = null;
Method ledCtrl = null;

……

try {
//iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));
Method getService = Class.forName(“android.os.ServiceManager”).getMethod(“getService”, String.class);
Object ledService = getService.invoke(null, “led”);
Method asInterface = Class.forName(“android.os.ILedService S t u b &quot; ) . g e t M e t h o d ( &quot; a s I n t e r f a c e &quot; , I B i n d e r . c l a s s ) ; p r o x y = a s I n t e r f a c e . i n v o k e ( n u l l , l e d S e r v i c e ) ; l e d C t r l = C l a s s . f o r N a m e ( &quot; a n d r o i d . o s . I L e d S e r v i c e Stub&quot;).getMethod(&quot;asInterface&quot;, IBinder.class); proxy = asInterface.invoke(null, ledService); ledCtrl = Class.forName(&quot;android.os.ILedService Stub").getMethod("asInterface",IBinder.class);proxy=asInterface.invoke(null,ledService);ledCtrl=Class.forName("android.os.ILedServiceStub$Proxy”).getMethod(“ledCtrl”, int.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
{% endcodeblock %}

使用:
{% codeblock lang:java %}
try {
ledCtrl.invoke(proxy, i, 1);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
{% endcodeblock %}

4.总结

如何实现一个硬件访问服务:
①编写Linux驱动led_drv.c,操作硬件;
②编写HAL层led_hal.c,访问Linux设备节点;
③编写JNI层com_android_server_LedService.cpp,调用HAL层,注册本地方法;
④修改onload.cpp注册服务,修改SystemServer.java加入服务;
⑤编写LedService.java调用本地方法;
⑥编写ILedService.aidl生成ILedService.java,提供接口;
⑦编写APP,实现UI、操作逻辑,调用接口访问硬件;

参考资料:
韦东山第四期Android驱动_Android系统
Android系统源代码情景分析_罗升阳
第一行代码:Android

涉及示例源码:
Github

从Galaxy S3到开发板:用Tiny4412复刻经典手机CPU的嵌入式开发体验 本文详细介绍了如何通过Tiny4412开发板复刻三星Galaxy S3的Exynos 4412处理器,探索嵌入式开发的实践与优化。从硬件架构解析到开发环境搭建,再到传感器集成和图形性能调优,为开发者提供了全面的技术指南。同时,文章还展示了该开发板在智能家居、工业视觉检测等物联网场景中的创新应用。 阅读详情

相关推荐

Tiny4412 LCD驱动(FB)

本文重点分析Tiny4412官网内核linux-3.5.0中的LCD驱动。

OneSea 871

Android访问硬件的方法

Android应用访问硬件的两种方法我们知道,Android应用由java编写,而java不能直接访问硬件,那么Android应用如何访问硬件? 一般有两个访问硬件的方法: JNI访问 硬件访问服务 JNI访问JAVA APP—>JNI_OnLoad()加载C库—->将JAVA三个地方法与C库函数进行关联并注册—->调用JAVA本地Native方法就可以访问C库的C接口——>进而访问硬件驱动中的o

小二的博客 5671

USB的24脚和12脚TYPEC引脚定义

USB 是一种接口,用于将设备连接到计算机上。通过该连接,计算机可以向设备发送数据或检索来自设备的数据。USB 为开发者提供了一个标准接口,适用于多种不同类型的应用。借助于系统的设计过程,USB 设备易于连接和使用。本应用笔记会使该过程变得更加简单。以下是本应用笔记中会使用的概念:USB 历史USB 架构USB 物理接口USB 速度USB 电源USB 端点USB 通信协议USB 描述符USB 类设备USB 枚举与配置过程。

易板 1万+

Android驱动(一)硬件访问服务学习之(一)Android通过JNI访问硬件

首先来看一下android的系统框图。google后期加入一层HAL层,硬件访问层 1.1  android访问硬件驱动的两种方式 (1)jni:多个设备同时访问,都可以来open吗? (2)硬件访问服务:发送请求服务—jni。不同硬件对应不同服务。. 1.2 LED应用开发 1.2.1开发框图 1.2.2编写android应用程序 (1)

风雨无阻 4567

数据可视化—power bi 基础操作汇总

数据来源: https://pan.baidu.com/s/1U_Y7rKfhnSKbLmh4FcUD_Q 提取码: yq5v 目录 基本功能与视图 编辑与查询—修改column案例 建立关联性 新增column与DAX完成总价运算 习题练习1:建立可视化图表 产品销售统计图 column拖曳可视化 1.基本功能与视图 ①基础操作界面 ②获取数据—以打开excel为例 ③数据文件基本视图 可视化形式:柱状图、数据、关联性 2.编辑与查询—修改column ①编辑与查询—数据处理界面 .

weixin_52730784的博客 2679

攻防世界GFSJ1184_welcome_CAT_CTF

由于它是 PIE 可执行文件,程序加载地址会随机化,这导致你在 GDB 调试时需要处理地址偏移(如 0x246d 变成 0x55555540246d)。:client 程序通过网络连接(get_nc() 和 get_flag(nc))与 server 通信,server 监听在端口 8888。:client 依赖外部共享库(如 libstdc++.so),因此运行时需要这些库存在系统中。程序就是wasd移动,走到不同标记(* $ @ )下方,输入j回车,会有不同反应。两个文件client和server。

拥抱AI的老粽子 1262

android使用服务访问硬件

1、源码目录和源码功能简要说明 1、首先在下面的目录下放入IIrRemoteService.aidl这个aidl接口然后编译,主要是为了生成IIrRemoteService.java接口 frameworks\base\core\java\android\os\IIrRemoteService.aidl 2.使用命令编译mmm frameworks/base,然后会生成IIrRemoteService.java ./out/soong/.intermediates/frameworks/base/fram

qq_27809619的博客 652

MRI多序列,多中心数据集预处理--使用FSL-Flirt 配准数据

FLIRT(FMRIB 的线性图像配准工具)是用于线性(仿射)模态内和模态间脑图像配准的全自动稳健且准确的工具。 什么是配准 在这两张图片上,很容易看出它们没有对齐,左边相对右边倾斜,虽然我们人类视觉系统还是能把对应的解剖关系连系在一起,但是我们使用计算机分析时确不能够 因此,我们希望相同的解剖结构对齐,用坐标表示的话,希望他们在相同的坐标上。如下图 对齐这些解剖结构的过程就叫做配准(register) 因此,配准的目的也是使相同的解剖结构在同一个位置,便于分析。可以将CT和MRI配准,看同一结构在两个

Tina姐的博客 1万+

Android12之service调试(一百五十二)

本篇目的:掌握service调试方法解决疑难问题。

Android系统攻城狮 1874

rknn-toolkit推理yolov5-face.rknn模型--python代码

import os import urllib import traceback import time import sys import numpy as np import cv2 from rknn.api import RKNN ONNX_MODEL = 'yolov5s.onnx' RKNN_MODEL = 'yolov5s.rknn' IMG_PATH = './bus.jpg' DATASET = './dataset.txt' QUANTIZE_ON = True BOX_THR.

C/C++、嵌入式开发、深度学习算法、模型部署与推理优化 3079

【STK】手把手教你利用STK进行关联分析仿真01-STK/CAT模块介绍

今天主要介绍接近分析工具CAT。

weixin_44184852的博客 1104

Android驱动(二)串口操作之tiny4412开发板实现串口通讯的两种方法

硬件平台:tiny4412系统:Android  5.0.2编译器: arm-linux-gcc-4.5.1 开发流程: (1)新建一个工程 (2)在main目录

风雨无阻 7745

Qt6.10 | Qt Bluetooth 蓝牙

本文介绍了QtBluetooth模块的功能和使用方法,包括蓝牙API概述、构建配置(CMake/qmake)、权限管理、平台支持(Linux/macOS特殊处理)、日志设置及许可信息。重点说明Linux平台的后端选择机制,以及macOS上的依赖限制。最后提及蓝牙聊天示例,展示RFCOMM协议通信的实现。全文提供了蓝牙开发的技术要点和注意事项。

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 399

Android驱动(一)硬件访问服务学习之(二)Android通过硬件访问服务访问硬件

首先来看一下硬件访问服务的框图: 一、硬件访问服务实现流程分析: (1)系统上电后,会执行SystemServer SystemServer.java有一个主函数,里面调用run方法。 public static void main(String[] args) { new SystemServer().run(); } 主函数执行run。Run

风雨无阻 4542

SAP采购申请审批策略配置简介

SAP采购申请审批策略配置简介 1、CT04创建特征

我小时候很黑的博客 8159

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

APP通过JNI调用直接访问LED灯DEMO

APP通过JNI调用直接访问LED灯DEMO一、实现一个简单的LED操作APP二、通过jni本地调用操作led灯 本章将实现一个简单的app通过JNI本地调用访问开发板LED灯的demo,用来理解App是如何来访问我们的开发板硬件的。 开发环境:Android stdio 3.5.3 开发板:tiny4412开发板 软件版本:Android5.0.2 + kernel3.0.86 一、实现一个简单...

TNT的博客 826

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

基于Tiny4412的电容屏多点触摸驱动程序的简单实现

一、基本开发环境和触摸芯片接口 1、基本开发环境 PC机:Ubuntu12.04 64bit GCC工具链条:arm-none-linux-gnueabi  (gcc version 4.5.1 (ctng-1.8.1-FA)) 开发板:友善之臂Tiny4412 板载系统:Android5.0.2 板载系统内核:Linux-3.0.86 2、触摸芯片接口 从上图中可以看出,

TECH_PRO的博客 2972

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

二、Tiny4412开发板运行安卓系统

1、想要在tiny4412开发板上运行安卓系统,首先需要一个boot文件去启动内核,内核在去启动安卓系统,也就是说咱们得下载uboot、kernel、android各自的文件到sd或者emmc中。   这里主要通过fastboot来烧写这些文件:具体步骤如下: 1、烧写uboot  fastboot flash fwbl1 E4412_N.bl1.bin   fastbo

Lidroid的博客 1607

Tiny4412 LED 硬件服务

1.Android系统中启动框架 2.首先实现驱动程序 #include <linux/kernel.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/device.h> #include <linux/...

adl30141的博客 174

ttysac1 java_基于Android的串口聊天室 (基于tiny4412) 一

一、平台介绍硬件平台: tiny4412ADK + S700 4GB FlashAndroid版本:Android-5.0.2Linux版本: Linux-3.0.86Bootloader:Superboot作者:彭东林邮箱:pengdonglin137@163.com二、代码目录下面的例子是基于Andrioid源码中自带的一个串口应用SerialChat,Android已经把与之相关的Fream...

weixin_39805387的博客 308

基于TINY4412的Andorid开发-------简单的LED灯控制【转】

阅读目录(Content)参考资料:《Andriod系统源代码情景分析》《嵌入式Linux系统开发完全手册_基于4412_上册》作者:彭东林邮箱:pengdonglin137@163.com平台介绍:主机:Win7 32位虚拟机:VMware10 + ubuntu-12.04.2-desktop-amd64Android版本: android-4.2.2_r1Linux内核版本:linux-3.5.0Bootloader: 友善之臂提供的Superboot4412.bin目标平台:tiny4412ADK

lmrylll的博客 975

Tiny4412的linux_kernel编译

为tiny4412编译官方提供的linux内核

qq_52360914的博客 431

【转载】基于TINY4412的Andorid开发-------简单的LED灯控制

阅读目录(Content) 一、编写驱动程序 二、编写代码测试驱动程序 三、编写HAL代码 四、编写Framework代码 五、编写JNI代码 六、编写App 参考资料: 《Andriod系统源代码情景分析》 《嵌入式Linux系统开发完全手册_基于4412_上册》 作者:彭东林 邮箱:pengdonglin137@163.com ...

adl30141的博客 178

Tiny4412——Android灯光系统

优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。 本博文对应地址: https://hceng.cn/2019/05/09/Tiny4412——Android灯光系统/#more 继续学习Android驱动,Android灯光系统。 1.思路分析 前面对3.2 Android硬件访问服务进行了详细的分析,知道了Android大致可以分为四层:...

hceng_linux 1496

tiny 4412 在android中访问c 库

由于arm板的硬件调用 要通过c程序 所以把c的硬件调用代码写成一个库给java调用 添加链接描述 配制好能让c库被调用的环境 应用界面 让里面的按键能被调用 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http...

weixin_43898067的博客 320
上一篇: C++学习笔记
下一篇: Tiny4412——Android灯光系统
hceng_blog
博客等级 码龄10年 263粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值