实验二. 板载LED灯控制实验


实验目的

  • 了解多核心平台底板设备电路结构;
  • 掌握Android Studio下编写NDK代码,实现LED灯控制;

实验环境

  • 硬件:CBT-EMB-MIP 实验平台,PC机,mini USB数据线;
  • 软件: Android Studio 2.2 或更高版本, Android Plugin for Gradle 版本 2.2.0 或更高版本,CMake;

实验内容

  • 编写NDK程序,实现对板载5个LED灯的控制。

实验原理

  LED硬件原理及驱动移植请参见Linux实验文档LED驱动移植章节。
  LED字符驱动程序(linux-3.5/drivers/char/4412_led.c)已动态编译进内核zImage里。 在Android文件系统中加载内核生成的module(4412_led.ko)会生成LED灯设备的文件(dev/leds)。
  在Linux 系统中,所有设备都是以文件的形式被打开并进行读/写操作的。本实验中通过编写JNI层代码调用POSIX的文件接口函数对底层设备进行操作,实现上层应用程序控制LED灯亮灭。

本实验JNI编程时需要用到以下几个文件操作函数

OPEN - 打开设备

  • 【函数原型】

       #include <fcntl.h>
       int open(const char *pathname, int flags); 
       int open(const char *pathname, int flags, mode_t mode);
    
  • 【功能】打开名为path 的文件或设备,成功打开后返回文件句柄。

  • 【参数】pathname:文件路径或设备名;flags:打开方式,可选值是表4.1中的一个值或几个值的组合。
打开方式 含义
O_RDONLY 只读方式打开
O_WRONLY 只写方式打开
O_RDWR 读写方式打开(等同于 O_RDONLY | O_WRONLY)
... ...
O_NONBLOCK 采用非阻塞文件I/O方式

表4.1 打开方式对照表

  • 【返回值】成功打开后返回文件句柄,失败返回-1。

IOCTL - 控制高低电平

  • 【函数原型】

          #include <unistd.h>
          int ioctl(int fildes, int request, /* arg */ ...);
          int ioctl(int fd, int ledState,int ledID);
    
  • 【功能】控制I/O设备(指定LED灯的亮灭)

  • 【参数】fildes:文件或设备句柄,通常由open函数返回;
    ledState:LED灯控制参数(0:灭/1:亮);ledID:5个led灯对应的ID。

  • 【返回值】成功返回0,失败返回错误码。

CLOSE - 关闭设备

  • 【函数原型】

          #include <unistd.h>
          int close(int fildes);
    
  • 【功能】关闭之前被打开的文件或设备

  • 【参数】fildes:文件或设备句柄,通常由open函数返回

  • 【返回值】成功返回0,失败返回-1。

JNI代码具体实现

在本实验中,需要使用1个设备文件:/dev/leds对多核心嵌入式平台的5个LED灯进行控制。

  1. 打开LED设备

     #define DEVICE_NAME    "/dev/leds"
     ...
     fd = open(DEVICE_NAME, O_RDWR);//打开设备   
    
  2. 调用IOCTL控制

     #define LED_ON 1
     #define LED_OFF 0
     ...
     ioctl(fd, LED_ON, ledID);  //打开指定ID的LED灯
     ...
     ioctl(fd, LED_OFF, ledID); //关闭指定ID的LED灯  
    
  3. 关闭LED设备

     close(fd);
    

实验步骤

在Android Studio下创建支持C/C++的新项目

创建步骤参见本章实验一,本实验例程中创建的项目如图5.1所示。 LED工程

图5.1 LED项目工程

更改库名及CMake构建脚本

  • MainActivity更改加载库名称,注释掉stringFromJNI()相关调用。
    static {
        //System.loadLibrary("native-lib");
        System.loadLibrary("leds-jni");
    }
    ...
    //TextView tv = (TextView) findViewById(R.id.sample_text);
    //tv.setText(stringFromJNI());
    ...
    //public native String stringFromJNI();
  • 修改CMake构建配置文件
add_library( # Sets the name of the library.
             leds-jni
                ...
             src/main/jni/leds-jni.c )
             ...
target_link_libraries( # Specifies the target library.
                       leds-jni

                       ${log-lib} )
  • 点击工具栏中的 Sync Project 应用更改。

MainActivity中添加LED操作的本地方法

//    public native String stringFromJNI();
    public native int ledsOpen();
    public native int ledsClose();
    public native int ledsControl(int ledID, int ledState);

在方法名中按下Alt+Enter,选择Create Function ... > Fix all 'Missing JNI function' problems in file选项。项目src\main目录下会生成jni文件夹及leds-jni.c原生源文件。

  • 删除文件夹cppnative-lib.cpp

  • 点击工具栏中的 Sync Project 应用更改。

编写原生源文件leds-jni.c

按照实验原理中的接口方法编写实现对应的打开、控制及关闭功能。

#include <jni.h>
#include <fcntl.h>
#include "android/log.h"

static const char *TAG = "libs";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

#define DEVICE_NAME    "/dev/leds"
#define LED_ON 1
#define LED_OFF 0
int fd;

JNIEXPORT jint JNICALL
Java_cbt_edu_iot_leds_MainActivity_ledsControl(JNIEnv *env, jobject instance, jint ledID,
                                               jint ledState) {

    int i = ledState;
    switch (i) {
        case LED_ON:
            ioctl(fd, LED_ON, ledID);
            break;

        case LED_OFF:
            ioctl(fd, LED_OFF, ledID);
            break;

        default:
            break;
    }
    return 1;

}

JNIEXPORT jint JNICALL
Java_cbt_edu_iot_leds_MainActivity_ledsClose(JNIEnv *env, jobject instance) {

    if (fd >= 0) {
        close(fd);
        fd = -1;
    }

}

JNIEXPORT jint JNICALL
Java_cbt_edu_iot_leds_MainActivity_ledsOpen(JNIEnv *env, jobject instance) {

    fd = open(DEVICE_NAME, O_RDWR);//打开设备
    if (fd == -1) {
        LOGI("open device %s error \n", DEVICE_NAME);
        return 0;
    }
    else {
        LOGI("open device %s ok! \n", DEVICE_NAME);
        return 1;
    }

}

编写UI界面及代码

  • 将光盘目录src\chapter4\experiment02\res目录下的drawablelayoutvalues三个文件夹拷贝至本项目工程的res目录下。

  • 打开activity_main.xml界面如图5.1所示: 主界面

图5.1 主界面

  • 编写MainActivity主程序,参考代码如下:
    public static final int LED_ON = 1;
    public static final int LED_OFF = 0;
    public static final int LED1 = 0;
    public static final int LED2 = 1;
    public static final int LED3 = 2;
    public static final int LED4 = 3;
    public static final int LED5 = 4;
    ImageView ivLed1, ivLed2, ivLed3, ivLed4, ivLed5;
    Switch swLed1, swLed2, swLed3, swLed4, swLed5;
    ...
  private void LayoutInit() {
        ivLed1 = (ImageView) findViewById(R.id.iv_led1);
        ...
        ivLed5 = (ImageView) findViewById(R.id.iv_led5);
        swLed1 = (Switch) findViewById(R.id.sw_led1);
        ...
        swLed5 = (Switch) findViewById(R.id.sw_led5);
        swLed1.setOnCheckedChangeListener(this);
        ...
        swLed5.setOnCheckedChangeListener(this);
    }

    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
        switch (compoundButton.getId()) {
            case R.id.sw_led1:
                if (b) {
                    ledsControl(LED1, LED_ON);
                    ivLed1.setImageResource(R.drawable.ic_led_on);
                } else {
                    ledsControl(LED1, LED_OFF);
                    ivLed1.setImageResource(R.drawable.ic_led_off);
                }
                break;
           ...
            case R.id.sw_led5:
                if (b) {
                    ledsControl(LED5, LED_ON);
                    ivLed5.setImageResource(R.drawable.ic_led_on);
                } else {
                    ledsControl(LED5, LED_OFF);
                    ivLed5.setImageResource(R.drawable.ic_led_off);
                }
                break;
        }

    }

具体实现细节可参考光盘目录下相应实验源码。

  • 构建和运行示例程序

操作应用程序之前需将多核心平台底板5路LED灯左侧的跳线帽跳到LED处,如图5.2所示:

LED跳线帽

图5.2 LED跳线帽

演示运行

运行程序后,首先点击加载驱动(modules)按键,会弹出如图5.3对话框,请求获取超级用户权限,点击授权

加载驱动

图5.3 加载驱动

点击右侧的打开按钮会调用ledsOpen()方法打开LED设备。 Android Studio界面下方的Android Monitor里的logcat会打印如下信息:

01-01 13:41:15.655 29662-29662/cbt.edu.iot.leds I/libs: open device /dev/leds ok!

之后便可以点击下方的Switch按钮,对5个LED灯进行控制,如图5.4所示:

LED灯控制

图5.4 LED灯控制

results matching ""

    No results matching ""