第 18 篇 综合项目实战:基于 RK3568 的安卓智能门禁系统,全栈开发

目录

一、项目需求与整体方案设计

[1. 核心功能需求](#1. 核心功能需求)

[2. 硬件方案选型(基于 RK3568)](#2. 硬件方案选型(基于 RK3568))

[3. 软件整体架构](#3. 软件整体架构)

二、第一步:全外设驱动适配与设备树配置

[1. 完整设备树配置](#1. 完整设备树配置)

[2. 驱动编译与验证](#2. 驱动编译与验证)

[三、第二步:HAL 层封装,统一硬件控制接口](#三、第二步:HAL 层封装,统一硬件控制接口)

[1. HAL 层目录与文件结构](#1. HAL 层目录与文件结构)

[2. 头文件door_access_hal.h](#2. 头文件door_access_hal.h)

[3. 接口实现door_access_hal.c](#3. 接口实现door_access_hal.c)

[4. 编译脚本Android.bp](#4. 编译脚本Android.bp)

[5. 编译 HAL 层模块](#5. 编译 HAL 层模块)

[四、第三步:JNI 封装与安卓门禁 App 开发](#四、第三步:JNI 封装与安卓门禁 App 开发)

[1. JNI 接口封装](#1. JNI 接口封装)

[2. 安卓门禁 App 开发](#2. 安卓门禁 App 开发)

五、项目优化与落地注意事项

结尾说两句


大家好,我是黒漂技术佬。前面 17 篇内容,我们从安卓驱动基础架构、设备树,到 GPIO、中断、I2C、SPI、显示、音频、摄像头等全系列外设驱动,再到全套调试方法,已经把 RK 安卓驱动开发的核心技能全部讲透了。

很多兄弟后台说:"佬,单个外设的驱动我都会了,但是怎么把这些东西整合到一个完整的项目里?有没有一个全流程的实战项目,能把所有知识点串起来?"

安排!今天这篇,我们就做一个工业级的落地项目 ------基于 RK3568 的安卓智能门禁系统,把前面所有学到的知识点全部整合起来,从需求分析、硬件方案选型、全外设驱动适配、HAL 层封装、JNI 接口开发,到最终的安卓门禁 App 全栈开发,一步不落,小白跟着走,就能做出一个可直接落地的智能门禁产品。


一、项目需求与整体方案设计

1. 核心功能需求

我们做的智能门禁系统,要实现工业级场景的完整功能,覆盖我们前面所有的知识点:

  1. 人脸识别开锁:通过摄像头采集人脸,本地 NPU 做人脸识别,匹配成功后驱动继电器开锁;
  2. 密码 / 刷卡开锁:支持电容按键输入密码、IC 卡刷卡开锁,适配门禁常用场景;
  3. 音视频对讲:支持门口机和室内机的音视频对讲,用到摄像头、音频编解码、屏幕显示;
  4. 门禁状态显示:7 寸 MIPI 屏实时显示摄像头画面、开锁状态、时间、提示信息;
  5. 开锁执行机构:继电器控制电磁锁,PWM 驱动蜂鸣器做按键 / 开锁提示音;
  6. 事件存储与上报:所有开锁、报警事件本地存储,支持网络上报到后台。

2. 硬件方案选型(基于 RK3568)

表格

功能模块 硬件选型 对应前面的知识点
核心主控 RK3568 核心板 + 底板 全系列驱动开发基础
显示模块 7 寸 MIPI LCD 屏(800*1280) 第 14 篇 MIPI 显示驱动、DRM 框架
摄像头 GC2053 1080P MIPI 摄像头 第 16 篇 摄像头驱动、V4L2 子系统
音频模块 ES8388 Codec + 咪头 + 喇叭 第 15 篇 音频驱动、ALSA 架构
开锁执行 5V 继电器 + 电磁锁 第 8 篇 GPIO 输出驱动
按键输入 6 路电容触摸按键 第 13 篇 input 子系统、按键中断驱动
提示音 无源蜂鸣器 第 10 篇 PWM 驱动
刷卡模块 MFRC522 RFID 模块(SPI 接口) 第 12 篇 SPI 驱动开发
存储 8GB EMMC 系统与事件存储
网络 双网口 + WiFi/BT 模块 系统联网与事件上报

3. 软件整体架构

我们严格遵循安卓系统的标准分层架构,和前面的单外设驱动开发完全一致,整体分为 5 层,从上到下依次是:

  1. 应用层:安卓门禁 App,实现 UI 交互、人脸识别逻辑、事件管理、音视频对讲;
  2. Framework 层:安卓系统 API + 自定义 JNI 接口,连接 Java 和 Native 层;
  3. HAL 层:硬件抽象层,封装所有外设的控制接口,给 JNI 层提供标准 C/C++ 接口;
  4. 内核驱动层:基于 Linux 内核,实现所有外设的标准驱动,给 HAL 层提供设备文件操作接口;
  5. 硬件层:RK3568 核心板 + 所有外设硬件。

二、第一步:全外设驱动适配与设备树配置

这个项目的核心基础,就是所有外设的驱动适配,我们基于前面的单外设驱动知识,完成整个项目的设备树配置和驱动适配,所有外设的驱动,内核都已经自带,我们只需要修改设备树,不用写一行驱动代码,就能完成所有外设的适配。

1. 完整设备树配置

打开你的 RK3568 板级.dts 文件,添加下面的完整设备树配置,所有模块都加了详细注释,直接对应我们的硬件方案:

dts

复制代码
/ {
    // ====================== 1. 门禁按键input驱动(6路电容按键) ======================
    gpio_keys: gpio-keys {
        compatible = "gpio-keys";
        status = "okay";
        autorepeat;
        pinctrl-names = "default";

        // 数字按键0-9、确认、取消,对应标准键码
        key_0 {
            label = "key-0";
            gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_0>;
            debounce-interval = <20>;
        };
        key_1 {
            label = "key-1";
            gpios = <&gpio1 RK_PA1 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_1>;
            debounce-interval = <20>;
        };
        key_2 {
            label = "key-2";
            gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_2>;
            debounce-interval = <20>;
        };
        key_3 {
            label = "key-3";
            gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_3>;
            debounce-interval = <20>;
        };
        key_enter {
            label = "key-enter";
            gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_ENTER>;
            debounce-interval = <20>;
            wakeup-source;
        };
        key_cancel {
            label = "key-cancel";
            gpios = <&gpio1 RK_PA5 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_ESC>;
            debounce-interval = <20>;
        };
    };

    // ====================== 2. 继电器开锁控制 ======================
    door_lock: door-lock {
        compatible = "gpio-leds";
        status = "okay";
        lock {
            label = "door-lock";
            gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
            default-state = "off"; // 默认断开,锁关闭
        };
    };

    // ====================== 3. 蜂鸣器PWM控制 ======================
    beeper: beeper {
        compatible = "pwm-beeper";
        status = "okay";
        pwms = <&pwm0 0 500000 0>; // 2kHz频率,适合蜂鸣器
        amp-supply = <&vcc3v3_sys>;
        beeper-hz = <2000>;
    };
};

// ====================== 4. MIPI 7寸屏显示驱动 ======================
&dsi0 {
    status = "okay";
    rockchip,lane-count = <2>;
    rockchip,max-bandwidth = <800>;
    rockchip,format = <MIPI_DSI_FMT_RGB888>;
    rockchip,mode = <MIPI_DSI_MODE_VIDEO>;
    rockchip,video-mode = <MIPI_DSI_VIDEO_MODE_BURST>;

    panel@0 {
        compatible = "simple-mipi-dsi-panel";
        reg = <0>;
        status = "okay";
        // 7寸屏800*1280时序参数,根据屏幕规格书修改
        display-timings {
            native-mode = <&timing0>;
            timing0: timing0 {
                clock-frequency = <78000000>;
                hactive = <800>;
                hfront-porch = <20>;
                hback-porch = <20>;
                hsync-len = <10>;
                vactive = <1280>;
                vfront-porch = <10>;
                vback-porch = <10>;
                vsync-len = <2>;
                hsync-active = <0>;
                vsync-active = <0>;
                de-active = <1>;
                pixelclk-active = <0>;
            };
        };
        power-supply = <&vcc3v3_lcd0>;
        reset-gpios = <&gpio4 RK_PA0 GPIO_ACTIVE_LOW>;
        reset-delay-ms = <10>;
        prepare-delay-ms = <10>;
        init-delay-ms = <120>;
        // 屏幕初始化命令,根据屏幕规格书添加
        dsi,init-commands = [
            15 00 02 78 00
            05 00 01 29 00
        ];
    };
};

&route_dsi0 {
    status = "okay";
    connect = <&vopb_out_dsi0>;
};

&vopb {
    status = "okay";
};

&vopb_out_dsi0 {
    status = "okay";
};

&backlight {
    status = "okay";
    pwms = <&pwm1 0 50000 0>;
    brightness-levels = <0 20 40 60 80 100 120 140 160 180 200 220 240 255>;
    default-brightness = <200>;
};

// ====================== 5. GC2053摄像头驱动 ======================
&i2c2 {
    status = "okay";
    clock-frequency = <400000>;

    gc2053: gc2053@37 {
        compatible = "galaxycore,gc2053";
        reg = <0x37>;
        status = "okay";
        clocks = <&cru CLK_CAM_OUT0>;
        clock-names = "xvclk";
        assigned-clocks = <&cru CLK_CAM_OUT0>;
        assigned-clock-rates = <24000000>;
        pwdn-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_HIGH>;
        reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_LOW>;
        rotation = <0>;
        orientation = <0>;
        data-lanes = <1 2>;
        max-fps = <30>;
        port {
            gc2053_out: endpoint {
                remote-endpoint = <&mipi_csi2_in>;
                data-lanes = <1 2>;
            };
        };
    };
};

&csi2_dphy0 {
    status = "okay";
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;
            mipi_csi2_in: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&gc2053_out>;
                data-lanes = <1 2>;
            };
        };
        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;
            csiphy_out: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&isp_in>;
            };
        };
    };
};

&rkcif_mipi_lvds {
    status = "okay";
    port {
        isp_in: endpoint {
            remote-endpoint = <&csiphy_out>;
        };
    };
};

&rkcif_mipi_lvds_sditf {
    status = "okay";
};

&rkisp_vir0 {
    status = "okay";
};

// ====================== 6. ES8388音频驱动 ======================
&i2c1 {
    status = "okay";
    clock-frequency = <400000>;

    es8388: es8388@10 {
        compatible = "everest,es8388";
        reg = <0x10>;
        status = "okay";
        #sound-dai-cells = <0>;
        clocks = <&cru CLK_I2S1_OUT>;
        clock-names = "mclk";
        hp-det-gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_LOW>;
    };
};

&i2s1_8ch {
    status = "okay";
    #sound-dai-cells = <0>;
    rockchip,clk-trcm = <1>;
};

sound {
    compatible = "simple-audio-card";
    status = "okay";
    model = "rk3568-door-access";
    simple-audio-card,format = "i2s";
    simple-audio-card,bitclock-master = <&dailink_master>;
    simple-audio-card,frame-master = <&dailink_master>;
    simple-audio-card,mclk-fs = <256>;

    dailink_master: simple-audio-card,cpu {
        sound-dai = <&i2s1_8ch>;
    };

    simple-audio-card,codec {
        sound-dai = <&es8388>;
    };
};

// ====================== 7. MFRC522 RFID模块(SPI) ======================
&spi0 {
    status = "okay";
    #address-cells = <1>;
    #size-cells = <0>;
    pinctrl-names = "default";
    pinctrl-0 = <&spi0_clk &spi0_mosi &spi0_miso &spi0_cs0>;

    mfrc522: mfrc522@0 {
        compatible = "nxp,mfrc522";
        reg = <0>;
        spi-max-frequency = <10000000>;
        status = "okay";
        reset-gpios = <&gpio2 RK_PA0 GPIO_ACTIVE_LOW>;
        interrupt-parent = <&gpio2>;
        interrupts = <RK_PA1 IRQ_TYPE_EDGE_FALLING>;
    };
};

// ====================== 8. 外设时钟使能 ======================
&pwm0 {
    status = "okay";
};

&pwm1 {
    status = "okay";
};

2. 驱动编译与验证

  1. 设备树修改完成后,编译内核和设备树: bash

    运行

    复制代码
    ./build.sh -CKu
  2. 烧录 boot.img 到开发板,重启后,按下面的步骤验证所有外设驱动是否正常: bash

    运行

    复制代码
    # 1. 验证按键input设备
    cat /proc/bus/input/devices | grep gpio-keys
    getevent # 按下按键,能看到对应的事件上报
    
    # 2. 验证MIPI屏
    # 开机屏幕正常点亮,显示安卓桌面,说明显示驱动正常
    
    # 3. 验证摄像头
    v4l2-ctl --list-devices # 能看到GC2053摄像头设备
    
    # 4. 验证音频
    aplay -l # 能看到声卡设备,tinyplay播放测试音频正常
    
    # 5. 验证继电器
    echo 1 > /sys/class/leds/door-lock/brightness # 继电器吸合,锁打开
    echo 0 > /sys/class/leds/door-lock/brightness # 继电器断开,锁关闭
    
    # 6. 验证蜂鸣器
    echo 2000 > /sys/class/input/eventX/device/beeper/hz # 蜂鸣器响

所有外设验证通过后,我们的驱动层就全部完成了,接下来进入 HAL 层的封装。


三、第二步:HAL 层封装,统一硬件控制接口

我们把所有外设的控制逻辑,封装成标准的 HAL 层动态库,给上层 JNI 提供统一的 C/C++ 接口,避免上层直接操作设备文件,符合安卓系统的架构规范。

1. HAL 层目录与文件结构

在 SDK 的hardware/rockchip/目录下,创建door_access_hal目录,创建以下文件:

plaintext

复制代码
door_access_hal/
├── include
│   └── door_access_hal.h  // 头文件,对外接口声明
├── door_access_hal.c       // 接口实现
└── Android.bp              // 编译脚本

2. 头文件door_access_hal.h

定义所有对外的控制接口,覆盖门禁系统的所有硬件操作:

c

运行

复制代码
#ifndef DOOR_ACCESS_HAL_H
#define DOOR_ACCESS_HAL_H

#ifdef __cplusplus
extern "C" {
#endif

// ====================== 门锁控制 ======================
// 开锁:1=开锁,0=关锁
int door_lock_set(int enable);
// 获取门锁状态
int door_lock_get_status(void);

// ====================== 蜂鸣器控制 ======================
// 蜂鸣器响:duration_ms=持续时间,0=一直响
int beeper_play(int duration_ms);
// 停止蜂鸣器
int beeper_stop(void);

// ====================== 按键事件读取 ======================
// 初始化按键事件监听
int key_init(void);
// 读取按键事件,阻塞等待
int key_read_event(int *key_code, int *state);
// 释放按键资源
int key_release(void);

// ====================== RFID读卡 ======================
// 初始化RFID模块
int rfid_init(void);
// 读取IC卡卡号,阻塞等待
int rfid_read_card(unsigned char *card_id, int *id_len);
// 释放RFID资源
int rfid_release(void);

// ====================== 音频播放 ======================
// 播放提示音,比如"请刷卡""密码错误"
int audio_play_tip(const char *audio_file);
// 开始录音
int audio_start_record(const char *record_file);
// 停止录音
int audio_stop_record(void);

// ====================== 系统初始化与释放 ======================
// 所有硬件初始化
int door_access_hal_init(void);
// 所有资源释放
int door_access_hal_release(void);

#ifdef __cplusplus
}
#endif

#endif // DOOR_ACCESS_HAL_H

3. 接口实现door_access_hal.c

基于我们前面的驱动设备文件,实现所有接口,核心代码片段如下:

c

运行

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <linux/input.h>
#include "door_access_hal.h"

// 全局设备文件描述符
static int lock_fd = -1;
static int key_fd = -1;
static int beeper_fd = -1;

// ====================== 门锁控制实现 ======================
int door_lock_set(int enable)
{
    if (lock_fd < 0) {
        lock_fd = open("/sys/class/leds/door-lock/brightness", O_RDWR);
        if (lock_fd < 0) {
            printf("【HAL】打开门锁设备失败\n");
            return -1;
        }
    }
    if (enable) {
        write(lock_fd, "1", 1);
        printf("【HAL】门锁已打开\n");
    } else {
        write(lock_fd, "0", 1);
        printf("【HAL】门锁已关闭\n");
    }
    return 0;
}

int door_lock_get_status(void)
{
    char buf[2] = {0};
    if (lock_fd < 0) return -1;
    lseek(lock_fd, 0, SEEK_SET);
    read(lock_fd, buf, 1);
    return atoi(buf);
}

// ====================== 蜂鸣器控制实现 ======================
int beeper_play(int duration_ms)
{
    // 蜂鸣器控制实现,通过sysfs节点控制PWM
    system("echo 2000 > /sys/class/leds/beeper/brightness");
    if (duration_ms > 0) {
        usleep(duration_ms * 1000);
        beeper_stop();
    }
    return 0;
}

int beeper_stop(void)
{
    system("echo 0 > /sys/class/leds/beeper/brightness");
    return 0;
}

// 剩余的按键、RFID、音频接口实现,基于前面的单外设驱动知识完成
// 音频部分基于tinyalsa库实现,RFID基于SPI设备文件操作

4. 编译脚本Android.bp

json

复制代码
cc_library_shared {
    name: "libdooraccess",
    srcs: ["door_access_hal.c"],
    local_include_dirs: ["include"],
    vendor: true,
    shared_libs: [
        "libtinyalsa",
        "libcutils",
        "libutils",
    ],
    cflags: [
        "-Wall",
        "-Werror",
    ],
}

5. 编译 HAL 层模块

bash

运行

复制代码
# 进入SDK根目录,设置编译环境
source build/envsetup.sh
lunch rk3568_r-userdebug
# 编译HAL模块
mmm hardware/rockchip/door_access_hal

编译完成后,会生成libdooraccess.so动态库,放到开发板的/vendor/lib64/目录下,给 JNI 层调用。


四、第三步:JNI 封装与安卓门禁 App 开发

1. JNI 接口封装

在 Android Studio 项目中,创建DoorAccessJni.java类,声明 native 方法,对应 HAL 层的接口:

java

运行

复制代码
package com.heipiao.dooraccess;

public class DoorAccessJni {
    static {
        System.loadLibrary("dooraccess");
        System.loadLibrary("door_jni");
    }

    public native int halInit();
    public native int halRelease();
    public native int doorLockSet(int enable);
    public native int beeperPlay(int durationMs);
    public native int readKeyEvent(int[] keyInfo);
    public native int readRfidCard(byte[] cardId);
    public native int playTipAudio(String audioFile);
}

然后编写对应的 JNI C++ 代码,调用 HAL 层的接口,完成 Java 和 Native 层的桥接。

2. 安卓门禁 App 开发

基于 JNI 接口,开发完整的门禁 App,核心功能包括:

  1. 实时预览:通过 Camera2 API,预览摄像头画面,做人脸识别;
  2. 人脸识别:集成 RK NPU 的人脸识别 SDK,本地完成人脸匹配,匹配成功自动开锁;
  3. 密码开锁:监听 input 按键事件,接收用户输入的密码,验证成功开锁;
  4. 刷卡开锁:监听 RFID 读卡事件,读取卡号,验证白名单后开锁;
  5. 事件记录:所有开锁、报警事件,本地存储到 SQLite 数据库,支持查询和导出;
  6. 音视频对讲:基于 WebRTC,实现门口机和室内机的音视频对讲。

App 的核心开锁逻辑代码片段:

java

运行

复制代码
// 人脸识别匹配成功回调
@Override
public void onFaceMatchSuccess(float similarity) {
    // 开锁
    doorAccessJni.doorLockSet(1);
    // 播放提示音
    doorAccessJni.beeperPlay(500);
    doorAccessJni.playTipAudio("/sdcard/door_open.mp3");
    // 记录事件
    EventManager.getInstance().addEvent("人脸识别开锁", "匹配度:" + similarity);
    // 3秒后关锁
    new Handler(Looper.getMainLooper()).postDelayed(() -> {
        doorAccessJni.doorLockSet(0);
    }, 3000);
}

// 按键事件监听
new Thread(() -> {
    int[] keyInfo = new int[2];
    while (isRunning) {
        int ret = doorAccessJni.readKeyEvent(keyInfo);
        if (ret == 0) {
            int keyCode = keyInfo[0];
            int state = keyInfo[1];
            // 处理按键输入,密码验证逻辑
            runOnUiThread(() -> handleKeyInput(keyCode, state));
        }
    }
}).start();

五、项目优化与落地注意事项

  1. 系统裁剪与开机优化:裁剪安卓系统不必要的服务,优化开机速度,实现开机自启门禁 App,工业级产品要求开机后 10 秒内进入工作状态;
  2. 稳定性优化:添加看门狗功能,系统异常时自动重启;所有硬件操作添加异常处理,避免 App 崩溃;
  3. 功耗优化:无人操作时,关闭屏幕背光,降低 CPU 频率,进入低功耗模式,有人按按键时唤醒;
  4. 安全加固:人脸数据、密码、卡号加密存储,安卓系统 root 权限关闭,SELinux 开启,防止恶意破解;
  5. 网络冗余:支持有线网和 WiFi 双网冗余,断网时本地正常工作,联网后自动上报事件。

结尾说两句

这篇文章,我们把前面 17 篇的所有知识点,全部整合到了一个完整的智能门禁项目里,从硬件方案、驱动适配、HAL 层封装,到安卓 App 开发,完成了全栈的落地实战。到这里,你已经具备了独立完成 RK 平台安卓工业级项目的全流程开发能力,再也不是只会写单外设驱动的脚本小子了。

下一篇,我们进入进阶优化内容,驱动性能优化与功耗优化实战,教你怎么让你的驱动运行更快、占用资源更少、功耗更低,满足工业级产品的严苛要求。

我是黒漂技术佬,关注我,带你零基础入门 RK 安卓驱动开发,不踩坑。有任何项目开发的问题,评论区留言,我都会一一回复。

相关推荐
UXbot3 小时前
APP原型生成工具测评
android·前端·人工智能·低代码·ios·开发·app原型
q***75183 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
泯仲3 小时前
从零起步学习MySQL || 第十五章:MySQL 可重复读隔离级别:它是如何工作的?是否完全解决幻读?
android·学习·mysql
qq_367719303 小时前
Android MQTT开源库paho.mqtt.android+MQTTX软件使用记录
android·java·开源·android mqtt开源库·mqttx软件使用
毕设源码-邱学长3 小时前
【开题答辩全过程】以 基于Android的仓库管理系统的设计与实现为例,包含答辩的问题和答案
android
2501_916008893 小时前
移动应用上架到应用商店的完整指南:原理与详细步骤
android·ios·小程序·https·uni-app·iphone·webview
草莓熊Lotso3 小时前
MySQL CRUD 核心指南:查询、插入、更新、删除全实战
android·开发语言·数据库·c++·人工智能·mysql
轩情吖3 小时前
MySQL之表的约束
android·数据库·c++·后端·mysql·开发·约束
Rainman博3 小时前
AMS-SplashScreen分析
android