From 2dcf9cc893a27c50d538baf8b5d47ff06fe495d8 Mon Sep 17 00:00:00 2001 From: android <android@lingyun.com> Date: Thu, 25 Jul 2024 09:42:53 +0800 Subject: [PATCH] Add Android apk SDK source code --- app_sdk/app_src/app/src/main/cpp/led_control.cpp | 160 + app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp | 0 app_sdk/app_src/.idea/.gitignore | 3 app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java | 12 app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp | 0 app_sdk/app_src/.idea/deploymentTargetSelector.xml | 18 app_sdk/app_src/.idea/gradle.xml | 19 app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml | 10 app_sdk/app_src/app/src/main/res/layout/activity_can.xml | 271 ++ app_sdk/app_src/app/build.gradle.kts | 63 app_sdk/app_src/app/src/main/res/drawable/background.xml | 8 app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml | 13 app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp | 0 app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp | 0 app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java | 18 app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml | 9 app_sdk/app_src/app/.gitignore | 1 app_sdk/app_src/app/src/main/AndroidManifest.xml | 39 app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp | 0 app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml | 17 app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml | 6 app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml | 10 app_sdk/app_src/app/src/main/cpp/CMakeLists.txt | 32 app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml | 9 app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar | 0 app_sdk/app_src/app/src/main/cpp/include/gpiod.h | 1775 +++++++++++++++ app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java | 12 app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml | 109 app_sdk/app_src/.idea/misc.xml | 9 app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so | 0 app_sdk/app_src/app/src/main/cpp/native-lib.cpp | 51 app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java | 347 ++ app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml | 30 app_sdk/app_src/app/src/main/res/layout/activity_main.xml | 87 app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp | 0 app_sdk/app_src/gradlew.bat | 89 app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java | 9 app_sdk/app_src/app/src/main/res/drawable/button_selector.xml | 7 app_sdk/app_src/.gitignore | 15 app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java | 111 app_sdk/app_src/gradlew | 185 + app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java | 247 ++ app_sdk/app_src/app/src/main/res/drawable/bt_right.xml | 12 app_sdk/app_src/.idea/vcs.xml | 6 app_sdk/app_src/app/src/main/res/drawable/bg2.jpg | 0 app_sdk/app_src/app/src/main/res/values/colors.xml | 14 app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp | 0 app_sdk/app_src/app/src/main/res/values-night/themes.xml | 16 app_sdk/app_src/app/src/main/res/values/themes.xml | 23 app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java | 9 app_sdk/app_src/app/src/main/res/drawable/bt_left.xml | 12 app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp | 0 app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties | 6 app_sdk/app_src/app/src/main/res/drawable/bg1.jpg | 0 app_sdk/app_src/gradle.properties | 21 app_sdk/app_src/app/src/main/res/drawable/bg_username.xml | 12 app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java | 69 app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java | 169 + app_sdk/app_src/app/src/main/res/xml/backup_rules.xml | 13 app_sdk/app_src/app/src/main/cpp/can_control.cpp | 193 + app_sdk/app_src/app/src/main/cpp/rs485_control.cpp | 516 ++++ app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java | 70 app_sdk/app_src/build.gradle.kts | 4 app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml | 423 +++ app_sdk/bin/rk3568test.apk | 0 app_sdk/app_src/gradle/libs.versions.toml | 22 app_sdk/app_src/app/src/main/res/layout/activity_adc.xml | 322 ++ app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp | 0 app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java | 17 app_sdk/app_src/app/src/main/cpp/adc_control.cpp | 522 ++++ app_sdk/app_src/app/src/main/res/values/strings.xml | 78 app_sdk/app_src/settings.gradle.kts | 23 app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp | 0 app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java | 26 app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java | 104 app_sdk/app_src/app/src/main/res/layout/activity_led.xml | 95 app_sdk/app_src/.idea/migrations.xml | 10 app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml | 14 app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp | 111 app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml | 13 app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 6 app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml | 170 + app_sdk/app_src/.idea/compiler.xml | 6 app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml | 19 app_sdk/app_src/app/src/main/res/layout/activity_menu.xml | 70 app_sdk/app_src/app/proguard-rules.pro | 21 86 files changed, 7,038 insertions(+), 0 deletions(-) diff --git a/app_sdk/app_src/.gitignore b/app_sdk/app_src/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/app_sdk/app_src/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/app_sdk/app_src/.idea/.gitignore b/app_sdk/app_src/.idea/.gitignore new file mode 100644 index 0000000..eaf91e2 --- /dev/null +++ b/app_sdk/app_src/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/app_sdk/app_src/.idea/compiler.xml b/app_sdk/app_src/.idea/compiler.xml new file mode 100644 index 0000000..8fabff5 --- /dev/null +++ b/app_sdk/app_src/.idea/compiler.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <bytecodeTargetLevel target="17" /> + </component> +</project> \ No newline at end of file diff --git a/app_sdk/app_src/.idea/deploymentTargetSelector.xml b/app_sdk/app_src/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..ba3e90b --- /dev/null +++ b/app_sdk/app_src/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="deploymentTargetSelector"> + <selectionStates> + <SelectionState runConfigName="app"> + <option name="selectionMode" value="DROPDOWN" /> + <DropdownSelection timestamp="2024-05-28T14:14:11.796854900Z"> + <Target type="DEFAULT_BOOT"> + <handle> + <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\圆圆圆\.android\avd\Medium_Phone_API_28.avd" /> + </handle> + </Target> + </DropdownSelection> + <DialogSelection /> + </SelectionState> + </selectionStates> + </component> +</project> \ No newline at end of file diff --git a/app_sdk/app_src/.idea/gradle.xml b/app_sdk/app_src/.idea/gradle.xml new file mode 100644 index 0000000..0b0534c --- /dev/null +++ b/app_sdk/app_src/.idea/gradle.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="GradleMigrationSettings" migrationVersion="1" /> + <component name="GradleSettings"> + <option name="linkedExternalProjectsSettings"> + <GradleProjectSettings> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" /> + <option name="modules"> + <set> + <option value="$PROJECT_DIR$" /> + <option value="$PROJECT_DIR$/app" /> + </set> + </option> + <option name="resolveExternalAnnotations" value="false" /> + </GradleProjectSettings> + </option> + </component> +</project> \ No newline at end of file diff --git a/app_sdk/app_src/.idea/migrations.xml b/app_sdk/app_src/.idea/migrations.xml new file mode 100644 index 0000000..48052b2 --- /dev/null +++ b/app_sdk/app_src/.idea/migrations.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectMigrations"> + <option name="MigrateToGradleLocalJavaHome"> + <set> + <option value="$PROJECT_DIR$" /> + </set> + </option> + </component> +</project> \ No newline at end of file diff --git a/app_sdk/app_src/.idea/misc.xml b/app_sdk/app_src/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/app_sdk/app_src/.idea/misc.xml @@ -0,0 +1,9 @@ +<project version="4"> + <component name="ExternalStorageConfigurationManager" enabled="true" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/build/classes" /> + </component> + <component name="ProjectType"> + <option name="id" value="Android" /> + </component> +</project> \ No newline at end of file diff --git a/app_sdk/app_src/.idea/vcs.xml b/app_sdk/app_src/.idea/vcs.xml new file mode 100644 index 0000000..9661ac7 --- /dev/null +++ b/app_sdk/app_src/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/app_sdk/app_src/app/.gitignore b/app_sdk/app_src/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app_sdk/app_src/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app_sdk/app_src/app/build.gradle.kts b/app_sdk/app_src/app/build.gradle.kts new file mode 100644 index 0000000..b0109c7 --- /dev/null +++ b/app_sdk/app_src/app/build.gradle.kts @@ -0,0 +1,63 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace = "com.example.serial" + compileSdk = 34 + + defaultConfig { + applicationId = "com.example.serial" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + ndk { + abiFilters.add("arm64-v8a") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + externalNativeBuild { + cmake { + path = file("src/main/cpp/CMakeLists.txt") + version = "3.22.1" + } + } + buildFeatures { + viewBinding = true + } + sourceSets { + getByName("main") { + jni { + srcDirs("src\\main\\jni", "src\\main\\jni") + } + } + } +} + +dependencies { + + implementation(libs.appcompat) + implementation(libs.material) + implementation(libs.constraintlayout) + implementation(libs.activity) + testImplementation(libs.junit) + androidTestImplementation(libs.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/app_sdk/app_src/app/proguard-rules.pro b/app_sdk/app_src/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app_sdk/app_src/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java b/app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java new file mode 100644 index 0000000..d400127 --- /dev/null +++ b/app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.serial; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.serial", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/AndroidManifest.xml b/app_sdk/app_src/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7103e33 --- /dev/null +++ b/app_sdk/app_src/app/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:sharedUserId="android.uid.system" + android:sharedUserMaxSdkVersion="32"> + + <application + android:allowBackup="true" + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupContent="@xml/backup_rules" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/Theme.Serial" + tools:targetApi="31" + android:requestLegacyExternalStorage="true"> + <activity + android:name=".MainActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name=".AdcActivity"/> + <activity android:name=".RS485Activity" /> + <activity android:name=".MenuActivity" /> + <activity android:name=".BuzzerActivity" /> + <activity android:name=".CanActivity" /> + <activity android:name=".LedActivity" /> + <activity android:name=".VisitRootfileActivity" /> + </application> + + <uses-permission android:name="android.permission." /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + +</manifest> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/cpp/CMakeLists.txt b/app_sdk/app_src/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..d2162ce --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,32 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html. +# For more examples on how to use CMake, see https://github.com/android/ndk-samples. + +# Sets the minimum CMake version required for this project. +cmake_minimum_required(VERSION 3.22.1) +project("serial") + +# 定义 libgpiod 目标并设置为 IMPORTED 类型 +add_library(libgpiod SHARED IMPORTED) + +# 设置 libgpiod 的 IMPORTED_LOCATION 属性 +set_target_properties(libgpiod + PROPERTIES IMPORTED_LOCATION + ${CMAKE_SOURCE_DIR}/../jni/${CMAKE_ANDROID_ARCH_ABI}/libgpiod.so) + +# 添加你的项目的目标 +add_library(${CMAKE_PROJECT_NAME} + SHARED # List C/C++ source files with relative paths to this CMakeLists.txt. + rs485_control.cpp + can_control.cpp + led_control.cpp + buzzer_control.cpp + adc_control.cpp + native-lib.cpp) + +# 将 libgpiod 链接到你的项目中 +target_link_libraries(${CMAKE_PROJECT_NAME} + # List libraries link to the target library + ${log-lib} + libgpiod + log) \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/cpp/adc_control.cpp b/app_sdk/app_src/app/src/main/cpp/adc_control.cpp new file mode 100644 index 0000000..02209a6 --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/adc_control.cpp @@ -0,0 +1,522 @@ +#include <jni.h> +#include <string> +#include <termios.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <android/log.h> +#include "include/gpiod.h" + +#define TAG "SerialApp" +#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) + +typedef struct comport_s +{ + char devname[12]; + unsigned int databit, parity, stopbit, flowctrl; + long baudrate; + + int fd; + int frag_size; +}comport_t; + +comport_t *adc_comport; + +// 将int类型的波特率转换成可以识别的波特率 +static speed_t getBaudRate(int baudRate){ + switch (baudRate) { + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } +} + +unsigned short crc_modbus(unsigned char *ptr, int len) +{ + unsigned int i; + unsigned short crc = 0xFFFF; //crc16位寄存器初始值 + + while(len--) + { + crc ^= *ptr++; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; //多项式 POLY(0x8005)的高低位交换值,这是由于其模型的一>些参数决定的 + else + crc = (crc >> 1); + } + } + + return crc; +} + +void calculate(char *result, unsigned char *data, int len) +{ + int i = 0; + unsigned char temp[6]; + char info[8]; + for(i=0; i<len; i++) + { + if(data[i] == 0xAA && data[i+1] == 0x55) + { + memset(temp, 0, sizeof(temp)); + if( (data[i+2] == 0x01 || data[i+2] == 0x02) && \ + (data[i+3] >= 0x00 && data[i+3] <= 0x03)) + { + // temp用于进行CRC校验 + temp[0] = data[i]; + temp[1] = data[i+1]; + temp[2] = data[i+2]; + temp[3] = data[i+3]; + temp[4] = data[i+4]; + temp[5] = data[i+5]; + //CRC,if right continue, else break. + uint16_t crc = crc_modbus(temp, 6); + printf("received buffer crc = %04X\n", crc); + if(data[i+6] != (crc&0xFF) || data[i+7] != ((crc>>8)&0xFF)) + { + LOGE("read failed: CRC failure. received CRC: %02X %02X, calculated CRC:%02X %02X\n", \ + temp[4], temp[5], (crc&0xFF), ((crc>>8)&0xFF)); + continue ; + } + + // calculate 43 02 ==> 0243 + memset(info, 0, sizeof(info)); + snprintf(info, sizeof(info), "%02x%02x", data[i+5], data[i+4]); + int length = sizeof(info) / sizeof(info[0]); + long long sum = 0; + printf("info=%s\n", info); + for(int j=0; j<strlen(info); j++) + { + char a = info[j]; + if(a >= '0' && a <= '9') + a = a - '0'; + else if(a >= 'A' && a <= 'F') + a = a - 'A' + 10; + else if(a >= 'a' && a <= 'f') + a = a - 'a' + 10; + int n = a << (4*(3-j)); + sum += n; + } + + char temp_res[350]; + memset(temp_res, 0, sizeof(temp_res)); + if(data[i+2] == 0x01) + { + strcat(result, "电流-"); + snprintf(temp_res, sizeof(temp_res), "通道%d=%.2fmA ", data[i+3], (double)sum*0.01); + } + else if(data[i+2] == 0x02) + { + strcat(result, "电压-"); + snprintf(temp_res, sizeof(temp_res), "通道%d=%.3fV ", data[i+3], (double)sum*0.001); + } + strcat(result, temp_res); + } + } + } +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_AdcControl_openComport(JNIEnv *env, jclass clazz, jstring path, + jlong baud_rate, jint data_bits, jint parity, + jint stop_bits, jint flow_control) { + // TODO: implement openComport() + speed_t speed; + struct termios old_cfg, new_cfg; + int old_flags; + long temp; + + adc_comport = (comport_t *) malloc(sizeof(comport_t)); + if (adc_comport == NULL) { + LOGE("Failed to allocate memory for comport structure."); + return -1; + } + memset(adc_comport, 0, sizeof(adc_comport)); + + // 检查波特率是否合法 + { + speed = getBaudRate(baud_rate); + if (speed == -1) + { + LOGE("Invalid buad rate"); + return -1; + } + } +// +// // 打开串口设备 + { + jboolean iscopy; + // 获取String字符串path中的UTF8编码,并将其转换成C中的字符串 + const char* devname = (*env).GetStringUTFChars(path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", devname, O_RDWR); + strncpy(adc_comport->devname, devname, 12); + adc_comport->baudrate = baud_rate; + adc_comport->fd = -1; + adc_comport->frag_size = 128; + adc_comport->databit = data_bits; + adc_comport->parity = parity; + adc_comport->flowctrl = flow_control; + adc_comport->stopbit = stop_bits; + + if( !strstr(adc_comport->devname, "tty") ) + { + LOGE("Open Not a tty device \" %s\" \n", adc_comport->devname); + return -2; + } + + adc_comport->fd = open(adc_comport->devname, O_RDWR); + if ( adc_comport->fd < 0 ) + { + if( strstr(strerror(errno), "Permission denied") != nullptr ) + return 1; + LOGE("open port failed:%s\n", strerror(errno)); + return -3; + } + LOGD("Open device %s", adc_comport->devname); + // 释放JNI字符串的UTF8编码 + (*env).ReleaseStringUTFChars(path, devname); + + if ( 0 != tcgetattr(adc_comport->fd, &old_cfg)) + { + LOGE("open port failed:%s\n", strerror(errno)); + return -6; + } + } +// + { + LOGD("starting configuring serial port"); + + old_cfg.c_cflag &= ~CSIZE; + old_cfg.c_cflag |= CREAD | CLOCAL; + old_cfg.c_lflag &= ~ICANON; + old_cfg.c_lflag &= ~ECHO; + old_cfg.c_lflag &= ~ECHOE; + old_cfg.c_lflag &= ~ECHONL; + old_cfg.c_lflag &= ~ISIG; + old_cfg.c_iflag &= ~(IXON | IXOFF | IXANY); + old_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); + old_cfg.c_oflag &= ~OPOST; + old_cfg.c_oflag &= ~ONLCR; + + + switch (adc_comport->databit) + { + case 7: + old_cfg.c_cflag |= CS7; + break; + case 6: + old_cfg.c_cflag |= CS6; + break; + case 5: + old_cfg.c_cflag |= CS5; + break; + case 8: + old_cfg.c_cflag |= CS8; + break; + } + + switch(adc_comport->parity) + { + case 1: + old_cfg.c_cflag |= (PARENB | PARODD); + old_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 2: + old_cfg.c_cflag |= PARENB; + old_cfg.c_cflag &= ~PARODD; + old_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0: + old_cfg.c_cflag &= ~PARENB; + break; + } + + if(1 != adc_comport->stopbit) + { + old_cfg.c_cflag |= CSTOPB; + } + else + { + old_cfg.c_cflag &= ~CSTOPB; + } + + // set flow control + // 1: software control; 2:hardware control; 0: none + switch(adc_comport->flowctrl) + { + case 1: + old_cfg.c_cflag &= ~(CRTSCTS); + old_cfg.c_iflag |= (IXON | IXOFF); + break; + case 2: + old_cfg.c_cflag |= CRTSCTS; + old_cfg.c_iflag &= ~(IXON | IXOFF); + break; + case 0: + old_cfg.c_cflag &= ~(CRTSCTS); + break; + } + + + // set baudRate + cfsetispeed(&old_cfg, speed); + cfsetospeed(&old_cfg, speed); + + old_cfg.c_cc[VTIME] = 10; + old_cfg.c_cc[VMIN] = 0; + + + // 将设置的串口参数应用到串口上,TCSANOW表示立即生效,(TCSADRAIN:在所有输出都被传输后生效;TCSAFLUSH:在所有输出都被传输后生效,同时丢弃所有未读取的输入) + if(tcsetattr(adc_comport->fd, TCSANOW, &old_cfg)) + { + LOGE("tcsetattr() failed:%s", strerror(errno)); + close(adc_comport->fd); + return -1; + } + LOGD("Connected device \" %s \" successfully\n", adc_comport->devname); + LOGD("port:%s, databit:%d, stopbit:%d, parity:%d, flowctl:%d", adc_comport->devname, adc_comport->databit, adc_comport->stopbit, adc_comport->parity, adc_comport->flowctrl); + } + + return 0; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_AdcControl_closeComport(JNIEnv *env, jclass clazz) { + // TODO: implement closeadc_comport() + if ( !adc_comport ) + { + LOGE("%s() get invalid input arguments.\n", __FUNCTION__ ); + return -1; + } + + if (adc_comport->fd >= 0) + { + close(adc_comport->fd); + LOGD("close device \" %s \" successfully\n", adc_comport->devname); + } + adc_comport->fd = -1; + free(adc_comport); + adc_comport = NULL; + return 0; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_AdcControl_sendToPort(JNIEnv *env, jclass clazz, jstring operate, + jstring channel) { + // TODO: implement sendToPort() + char *operator_native; + char *channel_native; + unsigned char data[6] = {0xAA, 0x55}; + int len = 2; //data的长度 + int rv = -1; + int i; + unsigned int byte; + + operator_native = (char *)env->GetStringUTFChars(operate, 0); + channel_native = (char *)env->GetStringUTFChars(channel, 0); + if(!operator_native || !channel_native) + { + LOGE("Invalid parameters\n"); + return -1; + } + + sscanf(operator_native, "%02X", &byte); + data[len++] = (unsigned char)byte; + + sscanf(channel_native, "%02X", &byte); + data[len++] = (unsigned char)byte; + + // 计算CRC16-MODBUS校验码 + uint16_t crc = crc_modbus(data, len); + data[len++] = crc & 0xFF; + data[len++] = (crc >> 8) & 0xFF; + + rv = write(adc_comport->fd, data, len); + if(rv < 0) + { + LOGE("write can data failed:%s", strerror(errno)); + close(adc_comport->fd); + return -2; + } + LOGD("write %d bytes can data success", rv); + for(i = 0; i < rv; i++) + { + LOGD("%02X ", data[i]); + } + + (*env).ReleaseStringUTFChars(operate, operator_native); + (*env).ReleaseStringUTFChars(channel, channel_native); + return 0; +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_example_serial_AdcControl_recvFromPort(JNIEnv *env, jclass clazz, jint len, jint timeout) { + // TODO: implement recvFromPort() + int rv = 0; + int iRet; + fd_set rdfds, exfds; + struct timeval stTime; + unsigned char nativeMsg[len]; + + memset(nativeMsg, 0, len); + + if( !adc_comport || len <= 0 ) + { + LOGE("Invalid parameter.\n"); + return nullptr; + } + if (adc_comport->fd == -1) + { + LOGE("Serail not connected.\n"); + return nullptr; + } + + FD_ZERO(&rdfds); + FD_ZERO(&exfds); + FD_SET(adc_comport->fd, &rdfds); + FD_SET(adc_comport->fd, &exfds); + + if (0xFFFFFFFF != timeout) + { + stTime.tv_sec = (time_t) (timeout / 1000); + stTime.tv_usec = (long)(1000 * (timeout % 1000)); + + iRet = select(adc_comport->fd + 1, &rdfds, 0, &exfds, &stTime); + if (0 == iRet) + { + return nullptr; + } + else if (0 < iRet) + { + if (0 != FD_ISSET(adc_comport->fd, &exfds)) + { + LOGE("Error checking recv status.\n"); + return nullptr; + } + + if (0 == FD_ISSET(adc_comport->fd, &rdfds)) + { + LOGE("No incoming data.\n"); + return nullptr; + } + } + else + { + if (EINTR == errno) + { + LOGE("catch interrupt signal.\n"); + } + else + { + LOGE("Check recv status failure.\n"); + } + return nullptr; + } + } + + usleep(10000); /* sleep for 10ms for data incoming */ + + // Get data from Comport + LOGD("start read"); + iRet = read(adc_comport->fd, nativeMsg, len); + if (0 > iRet) + { + return nullptr; + } + LOGD("Receive {%d} bytes data successfully\n", iRet); + for(int i=0; i<iRet; i++) + { + LOGD("%02X", nativeMsg[i]); + } + + char *tempHex = (char *) malloc(iRet * 3); + char *recvHex = (char *) malloc(iRet * 3 + 12); + if (tempHex == NULL || recvHex == NULL) + { + LOGE("Memory allocation failed: %s", strerror(errno)); + return nullptr; + } + memset(tempHex, 0, sizeof (tempHex)); + memset(recvHex, 0, sizeof (recvHex)); + for (int i = 0; i < iRet; i++) { + sprintf(tempHex + i * 3, "%02X ", nativeMsg[i]); + } + strcat(recvHex, "Hex result: "); + strcat(recvHex, tempHex); + + char recvalue[512]; + memset(recvalue, 0, sizeof (recvalue)); + calculate(recvalue, nativeMsg, iRet); + strcat(recvalue, recvHex); + LOGD("%s", recvalue); + return env->NewStringUTF(recvalue); +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp b/app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp new file mode 100644 index 0000000..92ff0b7 --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp @@ -0,0 +1,111 @@ +#include <jni.h> +#include <string> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <android/log.h> + +#define TAG "BuzzerApp" +#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) + +static char pwm_path[100]; + +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_BuzzerControl_pwmOpen(JNIEnv *env, jclass clazz, jstring id) { + // TODO: implement pwmOpen() + char *id_native; + char temp[100]; + int fd; + id_native = (char *)env->GetStringUTFChars(id, 0); + memset(pwm_path, 0, sizeof (pwm_path)); + snprintf(pwm_path, sizeof (pwm_path), "/sys/class/pwm/pwmchip%s/pwm0", id_native); + + memset(temp, 0, sizeof (temp)); + if(access(pwm_path, F_OK)) + { + LOGE("pwm_path:%s, access=%d", pwm_path, access(pwm_path, F_OK)); + snprintf(temp, sizeof (temp), "/sys/class/pwm/pwmchip%s/export", id_native); + fd = open(temp, O_WRONLY); + if(fd < 0) + { + if( strstr(strerror(errno), "Permission denied") != nullptr ) + { + LOGE("open %s error:%s\n", temp, strerror(errno)); + return 1; + } + LOGE("open %s error:%s\n", temp, strerror(errno)); + return -1; + } + + if(1 != write(fd, "0", 1)) + { + LOGE("Write '0' to pwmchip%s/export error\n", id_native); + close(fd); + return -2; + } + close(fd); + } + env->ReleaseStringUTFChars(id, id_native); + return 0; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_BuzzerControl_pwmConfig(JNIEnv *env, jclass clazz, jstring attr, + jstring val) { + // TODO: implement pwmConfig() + char file_path[100]; + char *attr_native; + char *val_native; + int len; + int fd = -1; + int ret; + + if(attr == NULL || val == NULL) + { + LOGE("argument invalid\n"); + return -1; + } + + attr_native = (char *)env->GetStringUTFChars(attr, 0); + val_native = (char *)env->GetStringUTFChars(val, 0); + + memset(file_path, 0, sizeof(file_path)); + snprintf(file_path, sizeof (file_path), "%s/%s", pwm_path, attr_native); + fd = open(file_path, O_WRONLY); + if (fd < 0) + { + if( strstr(strerror(errno), "Permission denied") != nullptr ) + { + LOGE("open %s error:%s\n", file_path, strerror(errno)); + fd=1; + } // 表示缺少权限 + LOGE("[%s] open %s error:%s\n", __FUNCTION__ , file_path, strerror(errno)); + return fd; + } + + len = strlen(val_native); + if(len != write(fd, val_native, len)) + { + if ( strstr(strerror(errno), "Invalid") != nullptr) + { + close(fd); + return 2; //表示输入的参数无效 + } + LOGE("[%s] write %s to %s error: %s\n", __FUNCTION__ , val_native, file_path, strerror(errno)); + close(fd); + return -2; + } + + close(fd); + env->ReleaseStringUTFChars(attr,attr_native); + env->ReleaseStringUTFChars(val, val_native); + return 0; +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/cpp/can_control.cpp b/app_sdk/app_src/app/src/main/cpp/can_control.cpp new file mode 100644 index 0000000..f81ffe5 --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/can_control.cpp @@ -0,0 +1,193 @@ +#include <jni.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <sys/socket.h> +#include <linux/can.h> +#include <linux/can/raw.h> +#include <android/log.h> +#include <fcntl.h> + +#define TAG "CanControl" +#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) + +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_CanControl_sendMessage(JNIEnv *env, jclass clazz, jstring id, jstring dlc, + jstring data, jstring ifname) { + // TODO: implement sendMessage() + int fd; + int i; + struct sockaddr_can addr; + struct ifreq ifr; + struct can_frame frame; + unsigned int byte; + char *token; + + const char *sendId = env->GetStringUTFChars(id, 0); + const char *sendDlc = env->GetStringUTFChars(dlc, 0); + const char *sendMsg = env->GetStringUTFChars(data, 0); + const char *if_name = env->GetStringUTFChars(ifname, 0); + if ( sendId == nullptr || sendDlc == nullptr || sendMsg == nullptr || if_name == nullptr) + { + LOGE("Invalid parameter"); + return -1; + } + + // create socket + fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if(fd < 0) + { + LOGE("create can socket failed:%s\n", strerror(errno)); + return -2; + } + + strcpy(ifr.ifr_name, if_name); + ioctl(fd, SIOCGIFINDEX, &ifr); + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + if (bind(fd, (struct sockaddr*)&addr, sizeof (addr)) < 0) + { + LOGE("bind socket failed:%s\n", strerror(errno)); + return -3; + } + sscanf(sendId, "%X", &byte); + frame.can_id = byte; + sscanf(sendDlc, "%u", &byte); + frame.can_dlc = byte; + token = strtok((char *)sendMsg, " "); + for (int i = 0; i < frame.can_dlc && token != NULL; ++i) + { + unsigned int data_byte; + sscanf(token, "%02X", &data_byte); + frame.data[i] = (unsigned char)data_byte; + token = strtok(NULL, " "); + } + + if (write(fd, &frame, sizeof (struct can_frame)) != sizeof (struct can_frame)) + { + LOGE("write data failed:%s\n", strerror(errno)); + return -4; + } + + LOGD("Send CAN frame: ID=0x%X DLC=%d data=", frame.can_id, frame.can_dlc); + for(int i = 0; i < frame.can_dlc; i++) + { + LOGD("%02X ", frame.data[i]); + } + printf("\n"); + + close(fd); + env->ReleaseStringUTFChars(id, sendId); + env->ReleaseStringUTFChars(dlc, sendDlc); + env->ReleaseStringUTFChars(data, sendMsg); + env->ReleaseStringUTFChars(ifname, if_name); + return 0; +} +extern "C" +JNIEXPORT jstring JNICALL +Java_com_example_serial_CanControl_receiveMessage(JNIEnv *env, jclass clazz, jstring ifname) { + // TODO: implement receiveMessage() + int fd; + int rv; + struct sockaddr_can addr; + struct ifreq ifr; + struct can_frame frame; + char recvMsg[128]; + char buf[4]; + + const char *if_name = env->GetStringUTFChars(ifname, 0); + if (if_name == nullptr) + { + LOGE("Invalid parameter"); + return nullptr; + } + + fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if(fd < 0) + { + LOGE("create can socket failed:%s\n", strerror(errno)); + return nullptr; + } + strcpy(ifr.ifr_name, if_name); + ioctl(fd, SIOCGIFINDEX, &ifr); + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + if (bind(fd, (struct sockaddr*)&addr, sizeof (addr)) < 0) + { + LOGE("bind socket failed:%s\n", strerror(errno)); + return nullptr; + } + + rv = read(fd, &frame, sizeof (struct can_frame)); + if (rv < 0) + { + LOGE("read data failed:%s\n", strerror(errno)); + return nullptr; + } else if (rv < sizeof (struct can_frame)) + { + LOGE("read incomplete CAN frame\n"); + return nullptr; + } + memset(recvMsg, 0, sizeof (recvMsg)); + sprintf(recvMsg, "ID=%X DLC=%d Data=", frame.can_id, frame.can_dlc); + for (int i = 0; i < frame.can_dlc; ++i) { + memset(buf, 0, sizeof (buf)); + sprintf(buf, "%02X ", frame.data[i]); + strcat(recvMsg, buf); + } + LOGD("recived: %s", recvMsg); + close(fd); + env->ReleaseStringUTFChars(ifname, if_name); + return env->NewStringUTF(recvMsg); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_CanControl_openCan(JNIEnv *env, jclass clazz, jstring ifname, + jstring bitrate) { + // TODO: implement openCan() + char *ifname_native; + char *bitrate_native; + char command[128]; + + ifname_native = (char *)env->GetStringUTFChars(ifname, 0); + bitrate_native = (char *)env->GetStringUTFChars(bitrate, 0); + + memset(command, 0, sizeof (command)); + snprintf(command, sizeof (command), "ip link set %s down", ifname_native); + if( system(command) == -1) + { + LOGE("system %s failure:%s\n", command, strerror(errno)); + return -1; + } + LOGD("system %s success\n", command); + + memset(command, 0, sizeof (command)); + snprintf(command, sizeof(command), "ip link set %s up type can bitrate %d", ifname_native, bitrate_native); + if( system(command) == -1) + { + LOGE("system %s failure:%s\n", command, strerror(errno)); + return -1; + } + LOGD("system %s success\n", command); + + memset(command, 0, sizeof (command)); + snprintf(command, sizeof(command), "ip link set %s up type can", ifname_native); + if( system(command) == -1) + { + LOGE("system %s failure:%s\n", command, strerror(errno)); + return -1; + } + LOGD("system %s success\n", command); + env->ReleaseStringUTFChars(ifname, ifname_native); + env->ReleaseStringUTFChars(bitrate, bitrate_native); + return 0; +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/cpp/include/gpiod.h b/app_sdk/app_src/app/src/main/cpp/include/gpiod.h new file mode 100644 index 0000000..3477f9d --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/include/gpiod.h @@ -0,0 +1,1775 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libgpiod. + * + * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com> + */ + +#ifndef __LIBGPIOD_GPIOD_H__ +#define __LIBGPIOD_GPIOD_H__ + +#include <stdbool.h> +#include <stdlib.h> +#include <time.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file gpiod.h + */ + +/** + * @mainpage libgpiod public API + * + * This is the complete documentation of the public API made available to + * users of libgpiod. + * + * <p>The public header is logically split into two high-level parts: the + * simple API and the low-level API. The former allows users to easily + * interact with the GPIOs in the system without dealing with the low-level + * data structures and resource control. The latter gives the user much more + * fine-grained control over the GPIO interface. + * + * <p>The low-level API is further logically split into several parts such + * as: GPIO chip & line operators, iterators, GPIO events handling etc. + * + * <p>General note on error handling: all routines exported by libgpiod set + * errno to one of the error values defined in errno.h upon failure. The way + * of notifying the caller that an error occurred varies between functions, + * but in general a function that returns an int, returns -1 on error, while + * a function returning a pointer bails out on error condition by returning + * a NULL pointer. + */ + +struct gpiod_chip; +struct gpiod_line; +struct gpiod_chip_iter; +struct gpiod_line_iter; +struct gpiod_line_bulk; + +/** + * @defgroup common Common helper macros + * @{ + * + * Commonly used utility macros. + */ + +/** + * @brief Makes symbol visible. + */ +#define GPIOD_API __attribute__((visibility("default"))) + +/** + * @brief Marks a function argument or variable as potentially unused. + */ +#define GPIOD_UNUSED __attribute__((unused)) + +/** + * @brief Shift 1 by given offset. + * @param nr Bit position. + * @return 1 shifted by nr. + */ +#define GPIOD_BIT(nr) (1UL << (nr)) + +/** + * @brief Marks a public function as deprecated. + */ +#define GPIOD_DEPRECATED __attribute__((deprecated)) + +/** + * @} + * + * @defgroup high_level High-level API + * @{ + * + * Simple high-level routines for straightforward GPIO manipulation without + * the need to use the gpiod_* structures or to keep track of resources. + */ + +/** + * @brief Miscellaneous GPIO flags. + */ +enum { + GPIOD_CTXLESS_FLAG_OPEN_DRAIN = GPIOD_BIT(0), + /**< The line is an open-drain port. */ + GPIOD_CTXLESS_FLAG_OPEN_SOURCE = GPIOD_BIT(1), + /**< The line is an open-source port. */ + GPIOD_CTXLESS_FLAG_BIAS_DISABLE = GPIOD_BIT(2), + /**< The line has neither either pull-up nor pull-down resistor */ + GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN = GPIOD_BIT(3), + /**< The line has pull-down resistor enabled */ + GPIOD_CTXLESS_FLAG_BIAS_PULL_UP = GPIOD_BIT(4), + /**< The line has pull-up resistor enabled */ +}; + +/** + * @brief Read current value from a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param offset Offset of the GPIO line. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @return 0 or 1 (GPIO value) if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_get_value(const char *device, unsigned int offset, + bool active_low, const char *consumer) GPIOD_API; + +/** + * @brief Read current value from a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param offset Offset of the GPIO line. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param flags The flags for the line. + * @return 0 or 1 (GPIO value) if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_get_value_ext(const char *device, unsigned int offset, + bool active_low, const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Read current values from a set of GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param offsets Array of offsets of lines whose values should be read. + * @param values Buffer in which the values will be stored. + * @param num_lines Number of lines, must be > 0. + * @param active_low The active state of the lines - true if low. + * @param consumer Name of the consumer. + * @return 0 if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_get_value_multiple(const char *device, + const unsigned int *offsets, int *values, + unsigned int num_lines, bool active_low, + const char *consumer) GPIOD_API; + +/** + * @brief Read current values from a set of GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param offsets Array of offsets of lines whose values should be read. + * @param values Buffer in which the values will be stored. + * @param num_lines Number of lines, must be > 0. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param flags The flags for the lines. + * @return 0 if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_get_value_multiple_ext(const char *device, + const unsigned int *offsets, + int *values, unsigned int num_lines, + bool active_low, const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Simple set value callback signature. + */ +typedef void (*gpiod_ctxless_set_value_cb)(void *); + +/** + * @brief Set value of a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param offset The offset of the GPIO line. + * @param value New value (0 or 1). + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * the value. Users can use this, for example, to pause the execution + * after toggling a GPIO. + * @param data Optional user data that will be passed to the callback function. + * @return 0 if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value, + bool active_low, const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data) GPIOD_API; + +/** + * @brief Set value of a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param offset The offset of the GPIO line. + * @param value New value (0 or 1). + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * the value. Users can use this, for example, to pause the execution + * after toggling a GPIO. + * @param data Optional user data that will be passed to the callback function. + * @param flags The flags for the line. + * @return 0 if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_set_value_ext(const char *device, unsigned int offset, + int value, bool active_low, + const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data, int flags) GPIOD_API; + +/** + * @brief Set values of multiple GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param offsets Array of offsets of lines the values of which should be set. + * @param values Array of integers containing new values. + * @param num_lines Number of lines, must be > 0. + * @param active_low The active state of the lines - true if low. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * all values. Works the same as in ::gpiod_ctxless_set_value. + * @param data Optional user data that will be passed to the callback function. + * @return 0 if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_set_value_multiple(const char *device, + const unsigned int *offsets, + const int *values, unsigned int num_lines, + bool active_low, const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data) GPIOD_API; + +/** + * @brief Set values of multiple GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param offsets Array of offsets of lines the values of which should be set. + * @param values Array of integers containing new values. + * @param num_lines Number of lines, must be > 0. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param cb Optional callback function that will be called right after setting + * all values. Works the same as in ::gpiod_ctxless_set_value. + * @param data Optional user data that will be passed to the callback function. + * @param flags The flags for the lines. + * @return 0 if the operation succeeds, -1 on error. + */ +int gpiod_ctxless_set_value_multiple_ext(const char *device, + const unsigned int *offsets, + const int *values, + unsigned int num_lines, + bool active_low, + const char *consumer, + gpiod_ctxless_set_value_cb cb, + void *data, int flags) GPIOD_API; + +/** + * @brief Event types that the ctxless event monitor can wait for. + */ +enum { + /**< Wait for rising edge events only. */ + GPIOD_CTXLESS_EVENT_RISING_EDGE = 1, + /**< Wait for falling edge events only. */ + GPIOD_CTXLESS_EVENT_FALLING_EDGE, + /**< Wait for both types of events. */ + GPIOD_CTXLESS_EVENT_BOTH_EDGES, +}; + +/** + * @brief Event types that can be passed to the ctxless event callback. + */ +enum { + GPIOD_CTXLESS_EVENT_CB_TIMEOUT = 1, + /**< Waiting for events timed out. */ + GPIOD_CTXLESS_EVENT_CB_RISING_EDGE, + /**< Rising edge event occured. */ + GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE, + /**< Falling edge event occured. */ +}; + +/** + * @brief Return status values that the ctxless event callback can return. + */ +enum { + GPIOD_CTXLESS_EVENT_CB_RET_ERR = -1, + /**< Stop processing events and indicate an error. */ + GPIOD_CTXLESS_EVENT_CB_RET_OK = 0, + /**< Continue processing events. */ + GPIOD_CTXLESS_EVENT_CB_RET_STOP = 1, + /**< Stop processing events. */ +}; + +/** + * @brief Simple event callback signature. + * + * The callback function takes the following arguments: event type (int), + * GPIO line offset (unsigned int), event timestamp (const struct timespec *) + * and a pointer to user data (void *). + * + * This callback is called by the ctxless event loop functions for each GPIO + * event. If the callback returns ::GPIOD_CTXLESS_EVENT_CB_RET_ERR, it should + * also set errno. + */ +typedef int (*gpiod_ctxless_event_handle_cb)(int, unsigned int, + const struct timespec *, void *); + +/** + * @brief Return status values that the ctxless event poll callback can return. + * + * Positive value returned from the polling callback indicates the number of + * events that occurred on the set of monitored lines. + */ +enum { + GPIOD_CTXLESS_EVENT_POLL_RET_STOP = -2, + /**< The event loop should stop processing events. */ + GPIOD_CTXLESS_EVENT_POLL_RET_ERR = -1, + /**< Polling error occurred (the polling function should set errno). */ + GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT = 0, + /**< Poll timed out. */ +}; + +/** + * @brief Helper structure for the ctxless event loop poll callback. + */ +struct gpiod_ctxless_event_poll_fd { + int fd; + /**< File descriptor number. */ + bool event; + /**< Indicates whether an event occurred on this file descriptor. */ +}; + +/** + * @brief Simple event poll callback signature. + * + * The poll callback function takes the following arguments: number of lines + * (unsigned int), an array of file descriptors on which input events should + * be monitored (struct gpiod_ctxless_event_poll_fd *), poll timeout + * (const struct timespec *) and a pointer to user data (void *). + * + * The callback should poll for input events on the set of descriptors and + * return an appropriate value that can be interpreted by the event loop + * routine. + */ +typedef int (*gpiod_ctxless_event_poll_cb)(unsigned int, + struct gpiod_ctxless_event_poll_fd *, + const struct timespec *, void *); + +/** + * @brief Wait for events on a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param offset GPIO line offset to monitor. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. + * @param event_cb Callback function to call for each line event. + * @param data User data passed to the callback. + * @return 0 if no errors were encountered, -1 if an error occurred. + * @note The way the ctxless event loop works is described in detail in + * ::gpiod_ctxless_event_loop_multiple - this is just a wrapper aound + * this routine which calls it for a single GPIO line. + * @deprecated This function suffers from an issue where HW may not allow + * setting up both rising and falling egde interrupts at the same + * time. + */ +int gpiod_ctxless_event_loop(const char *device, unsigned int offset, + bool active_low, const char *consumer, + const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) GPIOD_API GPIOD_DEPRECATED; + +/** + * @brief Wait for events on multiple GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param offsets Array of GPIO line offsets to monitor. + * @param num_lines Number of lines to monitor. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. Can + * be NULL. + * @param event_cb Callback function to call on event occurrence. + * @param data User data passed to the callback. + * @return 0 no errors were encountered, -1 if an error occurred. + * @note The poll callback can be NULL in which case the routine will fall + * back to a basic, ppoll() based callback. + * @deprecated This function suffers from an issue where HW may not allow + * setting up both rising and falling egde interrupts at the same + * time. + * + * Internally this routine opens the GPIO chip, requests the set of lines for + * both-edges events and calls the polling callback in a loop. The role of the + * polling callback is to detect input events on a set of file descriptors and + * notify the caller about the fds ready for reading. + * + * The ctxless event loop then reads each queued event from marked descriptors + * and calls the event callback. Both callbacks can stop the loop at any + * point. + * + * The poll_cb argument can be NULL in which case the function falls back to + * a default, ppoll() based callback. + */ +int gpiod_ctxless_event_loop_multiple(const char *device, + const unsigned int *offsets, + unsigned int num_lines, bool active_low, + const char *consumer, + const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) GPIOD_API GPIOD_DEPRECATED; + +/** + * @brief Wait for events on a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param event_type Type of events to listen for. + * @param offset GPIO line offset to monitor. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. + * @param event_cb Callback function to call for each line event. + * @param data User data passed to the callback. + * @return 0 if no errors were encountered, -1 if an error occurred. + * @note The way the ctxless event loop works is described in detail in + * ::gpiod_ctxless_event_monitor_multiple - this is just a wrapper aound + * this routine which calls it for a single GPIO line. + */ +int gpiod_ctxless_event_monitor(const char *device, int event_type, + unsigned int offset, bool active_low, + const char *consumer, + const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) GPIOD_API; + +/** + * @brief Wait for events on a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param event_type Type of events to listen for. + * @param offset GPIO line offset to monitor. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. + * @param event_cb Callback function to call for each line event. + * @param data User data passed to the callback. + * @param flags The flags for the line. + * @return 0 if no errors were encountered, -1 if an error occurred. + * @note The way the ctxless event loop works is described in detail in + * ::gpiod_ctxless_event_monitor_multiple - this is just a wrapper aound + * this routine which calls it for a single GPIO line. + */ +int gpiod_ctxless_event_monitor_ext(const char *device, int event_type, + unsigned int offset, bool active_low, + const char *consumer, + const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data, int flags) GPIOD_API; + +/** + * @brief Wait for events on multiple GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param event_type Type of events to listen for. + * @param offsets Array of GPIO line offsets to monitor. + * @param num_lines Number of lines to monitor. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. Can + * be NULL. + * @param event_cb Callback function to call on event occurrence. + * @param data User data passed to the callback. + * @return 0 no errors were encountered, -1 if an error occurred. + * @note The poll callback can be NULL in which case the routine will fall + * back to a basic, ppoll() based callback. + * + * Internally this routine opens the GPIO chip, requests the set of lines for + * the type of events specified in the event_type parameter and calls the + * polling callback in a loop. The role of the polling callback is to detect + * input events on a set of file descriptors and notify the caller about the + * fds ready for reading. + * + * The ctxless event loop then reads each queued event from marked descriptors + * and calls the event callback. Both callbacks can stop the loop at any + * point. + * + * The poll_cb argument can be NULL in which case the function falls back to + * a default, ppoll() based callback. + */ +int gpiod_ctxless_event_monitor_multiple( + const char *device, int event_type, + const unsigned int *offsets, + unsigned int num_lines, bool active_low, + const char *consumer, const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) GPIOD_API; + +/** + * @brief Wait for events on multiple GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param event_type Type of events to listen for. + * @param offsets Array of GPIO line offsets to monitor. + * @param num_lines Number of lines to monitor. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. Can + * be NULL. + * @param event_cb Callback function to call on event occurrence. + * @param data User data passed to the callback. + * @param flags The flags for the lines. + * @return 0 no errors were encountered, -1 if an error occurred. + * @note The poll callback can be NULL in which case the routine will fall + * back to a basic, ppoll() based callback. + * + * Internally this routine opens the GPIO chip, requests the set of lines for + * the type of events specified in the event_type parameter and calls the + * polling callback in a loop. The role of the polling callback is to detect + * input events on a set of file descriptors and notify the caller about the + * fds ready for reading. + * + * The ctxless event loop then reads each queued event from marked descriptors + * and calls the event callback. Both callbacks can stop the loop at any + * point. + * + * The poll_cb argument can be NULL in which case the function falls back to + * a default, ppoll() based callback. + */ +int gpiod_ctxless_event_monitor_multiple_ext( + const char *device, int event_type, + const unsigned int *offsets, + unsigned int num_lines, bool active_low, + const char *consumer, const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data, int flags) GPIOD_API; + + +/** + * @brief Determine the chip name and line offset of a line with given name. + * @param name The name of the GPIO line to lookup. + * @param chipname Buffer in which the name of the GPIO chip will be stored. + * @param chipname_size Size of the chip name buffer. + * @param offset Pointer to an integer in which the line offset will be stored. + * @return -1 on error, 0 if the line with given name doesn't exist and 1 if + * the line was found. In the first two cases the contents of chipname + * and offset remain unchanged. + * @note The chip name is truncated if the buffer can't hold its entire size. + * @attention GPIO line names are not unique in the linux kernel, neither + * globally nor within a single chip. This function finds the first + * line with given name. + */ +int gpiod_ctxless_find_line(const char *name, char *chipname, + size_t chipname_size, + unsigned int *offset) GPIOD_API; + +/** + * @} + * + * @defgroup chips GPIO chip operations + * @{ + * + * Functions and data structures dealing with GPIO chips. + */ + +/** + * @brief Open a gpiochip by path. + * @param path Path to the gpiochip device file. + * @return GPIO chip handle or NULL if an error occurred. + */ +struct gpiod_chip *gpiod_chip_open(const char *path) GPIOD_API; + +/** + * @brief Open a gpiochip by name. + * @param name Name of the gpiochip to open. + * @return GPIO chip handle or NULL if an error occurred. + * + * This routine appends name to '/dev/' to create the path. + */ +struct gpiod_chip *gpiod_chip_open_by_name(const char *name) GPIOD_API; + +/** + * @brief Open a gpiochip by number. + * @param num Number of the gpiochip. + * @return GPIO chip handle or NULL if an error occurred. + * + * This routine appends num to '/dev/gpiochip' to create the path. + */ +struct gpiod_chip *gpiod_chip_open_by_number(unsigned int num) GPIOD_API; + +/** + * @brief Open a gpiochip by label. + * @param label Label of the gpiochip to open. + * @return GPIO chip handle or NULL if the chip with given label was not found + * or an error occured. + * @note If the chip cannot be found but no other error occurred, errno is set + * to ENOENT. + */ +struct gpiod_chip *gpiod_chip_open_by_label(const char *label) GPIOD_API; + +/** + * @brief Open a gpiochip based on the best guess what the path is. + * @param descr String describing the gpiochip. + * @return GPIO chip handle or NULL if an error occurred. + * + * This routine tries to figure out whether the user passed it the path to the + * GPIO chip, its name, label or number as a string. Then it tries to open it + * using one of the gpiod_chip_open** variants. + */ +struct gpiod_chip *gpiod_chip_open_lookup(const char *descr) GPIOD_API; + +/** + * @brief Close a GPIO chip handle and release all allocated resources. + * @param chip The GPIO chip object. + */ +void gpiod_chip_close(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the GPIO chip name as represented in the kernel. + * @param chip The GPIO chip object. + * @return Pointer to a human-readable string containing the chip name. + */ +const char *gpiod_chip_name(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the GPIO chip label as represented in the kernel. + * @param chip The GPIO chip object. + * @return Pointer to a human-readable string containing the chip label. + */ +const char *gpiod_chip_label(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the number of GPIO lines exposed by this chip. + * @param chip The GPIO chip object. + * @return Number of GPIO lines. + */ +unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the handle to the GPIO line at given offset. + * @param chip The GPIO chip object. + * @param offset The offset of the GPIO line. + * @return Pointer to the GPIO line handle or NULL if an error occured. + */ +struct gpiod_line * +gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset) GPIOD_API; + +/** + * @brief Retrieve a set of lines and store them in a line bulk object. + * @param chip The GPIO chip object. + * @param offsets Array of offsets of lines to retrieve. + * @param num_offsets Number of lines to retrieve. + * @param bulk Line bulk object in which to store the line handles. + * @return 0 on success, -1 on error. + */ +int gpiod_chip_get_lines(struct gpiod_chip *chip, + unsigned int *offsets, unsigned int num_offsets, + struct gpiod_line_bulk *bulk) GPIOD_API; + +/** + * @brief Retrieve all lines exposed by a chip and store them in a bulk object. + * @param chip The GPIO chip object. + * @param bulk Line bulk object in which to store the line handles. + * @return 0 on success, -1 on error. + */ +int gpiod_chip_get_all_lines(struct gpiod_chip *chip, + struct gpiod_line_bulk *bulk) GPIOD_API; + +/** + * @brief Find a GPIO line by name among lines associated with given GPIO chip. + * @param chip The GPIO chip object. + * @param name The name of the GPIO line. + * @return Pointer to the GPIO line handle or NULL if the line could not be + * found or an error occurred. + * @note In case a line with given name is not associated with given chip, the + * function sets errno to ENOENT. + * @attention GPIO line names are not unique in the linux kernel, neither + * globally nor within a single chip. This function finds the first + * line with given name. + */ +struct gpiod_line * +gpiod_chip_find_line(struct gpiod_chip *chip, const char *name) GPIOD_API; + +/** + * @brief Find a set of GPIO lines by names among lines exposed by this chip. + * @param chip The GPIO chip object. + * @param names Array of pointers to C-strings containing the names of the + * lines to lookup. Must end with a NULL-pointer. + * @param bulk Line bulk object in which the located lines will be stored. + * @return 0 if all lines were located, -1 on error. + * @note If at least one line from the list could not be found among the lines + * exposed by this chip, the function sets errno to ENOENT. + * @attention GPIO line names are not unique in the linux kernel, neither + * globally nor within a single chip. This function finds the first + * line with given name. + */ +int gpiod_chip_find_lines(struct gpiod_chip *chip, const char **names, + struct gpiod_line_bulk *bulk) GPIOD_API; + +/** + * @} + * + * @defgroup lines GPIO line operations + * @{ + * + * Functions and data structures dealing with GPIO lines. + * + * @defgroup line_bulk Operating on multiple lines + * @{ + * + * Convenience data structures and helper functions for storing and operating + * on multiple lines at once. + */ + +/** + * @brief Maximum number of GPIO lines that can be requested at once. + */ +#define GPIOD_LINE_BULK_MAX_LINES 64 + +/** + * @brief Helper structure for storing a set of GPIO line objects. + * + * This structure is used in all operations involving sets of GPIO lines. If + * a bulk object is being passed to a function while containing zero lines, + * the result is undefined. + */ +struct gpiod_line_bulk { + struct gpiod_line *lines[GPIOD_LINE_BULK_MAX_LINES]; + /**< Buffer for line pointers. */ + unsigned int num_lines; + /**< Number of lines currently held in this structure. */ +}; + +/** + * @brief Static initializer for GPIO bulk objects. + * + * This macro simply sets the internally held number of lines to 0. + */ +#define GPIOD_LINE_BULK_INITIALIZER { { NULL }, 0 } + +/** + * @brief Initialize a GPIO bulk object. + * @param bulk Line bulk object. + * + * This routine simply sets the internally held number of lines to 0. + */ +static inline void gpiod_line_bulk_init(struct gpiod_line_bulk *bulk) +{ + bulk->num_lines = 0; +} + +/** + * @brief Add a single line to a GPIO bulk object. + * @param bulk Line bulk object. + * @param line Line to add. + */ +static inline void gpiod_line_bulk_add(struct gpiod_line_bulk *bulk, + struct gpiod_line *line) +{ + bulk->lines[bulk->num_lines++] = line; +} + +/** + * @brief Retrieve the line handle from a line bulk object at given offset. + * @param bulk Line bulk object. + * @param offset Line offset. + * @return Line handle at given offset. + */ +static inline struct gpiod_line * +gpiod_line_bulk_get_line(struct gpiod_line_bulk *bulk, unsigned int offset) +{ + return bulk->lines[offset]; +} + +/** + * @brief Retrieve the number of GPIO lines held by this line bulk object. + * @param bulk Line bulk object. + * @return Number of lines held by this line bulk. + */ +static inline unsigned int +gpiod_line_bulk_num_lines(struct gpiod_line_bulk *bulk) +{ + return bulk->num_lines; +} + +/** + * @brief Iterate over all line handles held by a line bulk object. + * @param bulk Line bulk object. + * @param line GPIO line handle. On each iteration, the subsequent line handle + * is assigned to this pointer. + * @param lineptr Pointer to a GPIO line handle used to store the loop state. + */ +#define gpiod_line_bulk_foreach_line(bulk, line, lineptr) \ + for ((lineptr) = (bulk)->lines, (line) = *(lineptr); \ + (lineptr) <= (bulk)->lines + ((bulk)->num_lines - 1); \ + (lineptr)++, (line) = *(lineptr)) + +/** + * @brief Iterate over all line handles held by a line bulk object (integer + * counter variant). + * @param bulk Line bulk object. + * @param line GPIO line handle. On each iteration, the subsequent line handle + * is assigned to this pointer. + * @param offset An integer variable used to store the loop state. + * + * This is a variant of ::gpiod_line_bulk_foreach_line which uses an integer + * variable (either signed or unsigned) to store the loop state. This offset + * variable is guaranteed to correspond to the offset of the current line in + * the bulk->lines array. + */ +#define gpiod_line_bulk_foreach_line_off(bulk, line, offset) \ + for ((offset) = 0, (line) = (bulk)->lines[0]; \ + (offset) < (bulk)->num_lines; \ + (offset)++, (line) = (bulk)->lines[(offset)]) + +/** + * @} + * + * @defgroup line_info Line info + * @{ + * + * Definitions and functions for retrieving kernel information about both + * requested and free lines. + */ + +/** + * @brief Possible direction settings. + */ +enum { + GPIOD_LINE_DIRECTION_INPUT = 1, + /**< Direction is input - we're reading the state of a GPIO line. */ + GPIOD_LINE_DIRECTION_OUTPUT, + /**< Direction is output - we're driving the GPIO line. */ +}; + +/** + * @brief Possible active state settings. + */ +enum { + GPIOD_LINE_ACTIVE_STATE_HIGH = 1, + /**< The active state of a GPIO is active-high. */ + GPIOD_LINE_ACTIVE_STATE_LOW, + /**< The active state of a GPIO is active-low. */ +}; + +/** + * @brief Possible internal bias settings. + */ +enum { + GPIOD_LINE_BIAS_AS_IS = 1, + /**< The internal bias state is unknown. */ + GPIOD_LINE_BIAS_DISABLE, + /**< The internal bias is disabled. */ + GPIOD_LINE_BIAS_PULL_UP, + /**< The internal pull-up bias is enabled. */ + GPIOD_LINE_BIAS_PULL_DOWN, + /**< The internal pull-down bias is enabled. */ +}; + +/** + * @brief Read the GPIO line offset. + * @param line GPIO line object. + * @return Line offset. + */ +unsigned int gpiod_line_offset(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line name. + * @param line GPIO line object. + * @return Name of the GPIO line as it is represented in the kernel. This + * routine returns a pointer to a null-terminated string or NULL if + * the line is unnamed. + */ +const char *gpiod_line_name(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line consumer name. + * @param line GPIO line object. + * @return Name of the GPIO consumer name as it is represented in the + * kernel. This routine returns a pointer to a null-terminated string + * or NULL if the line is not used. + */ +const char *gpiod_line_consumer(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line direction setting. + * @param line GPIO line object. + * @return Returns GPIOD_LINE_DIRECTION_INPUT or GPIOD_LINE_DIRECTION_OUTPUT. + */ +int gpiod_line_direction(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line active state setting. + * @param line GPIO line object. + * @return Returns GPIOD_LINE_ACTIVE_STATE_HIGH or GPIOD_LINE_ACTIVE_STATE_LOW. + */ +int gpiod_line_active_state(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line bias setting. + * @param line GPIO line object. + * @return Returns GPIOD_LINE_BIAS_PULL_UP, GPIOD_LINE_BIAS_PULL_DOWN, + * GPIOD_LINE_BIAS_DISABLE or GPIOD_LINE_BIAS_AS_IS. + */ +int gpiod_line_bias(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line is currently in use. + * @param line GPIO line object. + * @return True if the line is in use, false otherwise. + * + * The user space can't know exactly why a line is busy. It may have been + * requested by another process or hogged by the kernel. It only matters that + * the line is used and we can't request it. + */ +bool gpiod_line_is_used(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line is an open-drain GPIO. + * @param line GPIO line object. + * @return True if the line is an open-drain GPIO, false otherwise. + */ +bool gpiod_line_is_open_drain(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line is an open-source GPIO. + * @param line GPIO line object. + * @return True if the line is an open-source GPIO, false otherwise. + */ +bool gpiod_line_is_open_source(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Re-read the line info. + * @param line GPIO line object. + * @return 0 if the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * The line info is initially retrieved from the kernel by + * gpiod_chip_get_line() and is later re-read after every successful request. + * Users can use this function to manually re-read the line info when needed. + * + * We currently have no mechanism provided by the kernel for keeping the line + * info synchronized and for the sake of speed and simplicity of this low-level + * library we don't want to re-read the line info automatically everytime + * a property is retrieved. Any daemon using this library must track the state + * of lines on its own and call this routine if needed. + * + * The state of requested lines is kept synchronized (or rather cannot be + * changed by external agents while the ownership of the line is taken) so + * there's no need to call this function in that case. + */ +int gpiod_line_update(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line info needs to be updated. + * @param line GPIO line object. + * @return Always returns false. + * @deprecated This mechanism no longer exists in the library and this function + * does nothing. + */ +bool +gpiod_line_needs_update(struct gpiod_line *line) GPIOD_API GPIOD_DEPRECATED; + +/** + * @} + * + * @defgroup line_request Line requests + * @{ + * + * Interface for requesting GPIO lines from userspace for both values and + * events. + */ + +/** + * @brief Available types of requests. + */ +enum { + GPIOD_LINE_REQUEST_DIRECTION_AS_IS = 1, + /**< Request the line(s), but don't change current direction. */ + GPIOD_LINE_REQUEST_DIRECTION_INPUT, + /**< Request the line(s) for reading the GPIO line state. */ + GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, + /**< Request the line(s) for setting the GPIO line state. */ + GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE, + /**< Only watch falling edge events. */ + GPIOD_LINE_REQUEST_EVENT_RISING_EDGE, + /**< Only watch rising edge events. */ + GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES, + /**< Monitor both types of events. */ +}; + +/** + * @brief Miscellaneous GPIO request flags. + */ +enum { + GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN = GPIOD_BIT(0), + /**< The line is an open-drain port. */ + GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE = GPIOD_BIT(1), + /**< The line is an open-source port. */ + GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW = GPIOD_BIT(2), + /**< The active state of the line is low (high is the default). */ + GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE = GPIOD_BIT(3), + /**< The line has neither either pull-up nor pull-down resistor. */ + GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN = GPIOD_BIT(4), + /**< The line has pull-down resistor enabled. */ + GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP = GPIOD_BIT(5), + /**< The line has pull-up resistor enabled. */ +}; + +/** + * @brief Structure holding configuration of a line request. + */ +struct gpiod_line_request_config { + const char *consumer; + /**< Name of the consumer. */ + int request_type; + /**< Request type. */ + int flags; + /**< Other configuration flags. */ +}; + +/** + * @brief Reserve a single line. + * @param line GPIO line object. + * @param config Request options. + * @param default_val Initial line value - only relevant if we're setting + * the direction to output. + * @return 0 if the line was properly reserved. In case of an error this + * routine returns -1 and sets the last error number. + * + * If this routine succeeds, the caller takes ownership of the GPIO line until + * it's released. + */ +int gpiod_line_request(struct gpiod_line *line, + const struct gpiod_line_request_config *config, + int default_val) GPIOD_API; + +/** + * @brief Reserve a single line, set the direction to input. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @return 0 if the line was properly reserved, -1 on failure. + */ +int gpiod_line_request_input(struct gpiod_line *line, + const char *consumer) GPIOD_API; + +/** + * @brief Reserve a single line, set the direction to output. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param default_val Initial line value. + * @return 0 if the line was properly reserved, -1 on failure. + */ +int gpiod_line_request_output(struct gpiod_line *line, + const char *consumer, int default_val) GPIOD_API; + +/** + * @brief Request rising edge event notifications on a single line. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_rising_edge_events(struct gpiod_line *line, + const char *consumer) GPIOD_API; + +/** + * @brief Request falling edge event notifications on a single line. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_falling_edge_events(struct gpiod_line *line, + const char *consumer) GPIOD_API; + +/** + * @brief Request all event type notifications on a single line. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_both_edges_events(struct gpiod_line *line, + const char *consumer) GPIOD_API; + +/** + * @brief Reserve a single line, set the direction to input. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the line was properly reserved, -1 on failure. + */ +int gpiod_line_request_input_flags(struct gpiod_line *line, + const char *consumer, int flags) GPIOD_API; + +/** + * @brief Reserve a single line, set the direction to output. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @param default_val Initial line value. + * @return 0 if the line was properly reserved, -1 on failure. + */ +int gpiod_line_request_output_flags(struct gpiod_line *line, + const char *consumer, int flags, + int default_val) GPIOD_API; + +/** + * @brief Request rising edge event notifications on a single line. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line, + const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Request falling edge event notifications on a single line. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line, + const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Request all event type notifications on a single line. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_both_edges_events_flags(struct gpiod_line *line, + const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Reserve a set of GPIO lines. + * @param bulk Set of GPIO lines to reserve. + * @param config Request options. + * @param default_vals Initial line values - only relevant if we're setting + * the direction to output. + * @return 0 if the all lines were properly requested. In case of an error + * this routine returns -1 and sets the last error number. + * + * If this routine succeeds, the caller takes ownership of the GPIO lines + * until they're released. All the requested lines must be prodivided by the + * same gpiochip. + */ +int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, + const struct gpiod_line_request_config *config, + const int *default_vals) GPIOD_API; + +/** + * @brief Reserve a set of GPIO lines, set the direction to input. + * @param bulk Set of GPIO lines to reserve. + * @param consumer Name of the consumer. + * @return 0 if the lines were properly reserved, -1 on failure. + */ +int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk, + const char *consumer) GPIOD_API; + +/** + * @brief Reserve a set of GPIO lines, set the direction to output. + * @param bulk Set of GPIO lines to reserve. + * @param consumer Name of the consumer. + * @param default_vals Initial line values. + * @return 0 if the lines were properly reserved, -1 on failure. + */ +int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk, + const char *consumer, + const int *default_vals) GPIOD_API; + +/** + * @brief Request rising edge event notifications on a set of lines. + * @param bulk Set of GPIO lines to request. + * @param consumer Name of the consumer. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_bulk_rising_edge_events(struct gpiod_line_bulk *bulk, + const char *consumer) GPIOD_API; + +/** + * @brief Request falling edge event notifications on a set of lines. + * @param bulk Set of GPIO lines to request. + * @param consumer Name of the consumer. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_bulk_falling_edge_events(struct gpiod_line_bulk *bulk, + const char *consumer) GPIOD_API; + +/** + * @brief Request all event type notifications on a set of lines. + * @param bulk Set of GPIO lines to request. + * @param consumer Name of the consumer. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_bulk_both_edges_events(struct gpiod_line_bulk *bulk, + const char *consumer) GPIOD_API; + +/** + * @brief Reserve a set of GPIO lines, set the direction to input. + * @param bulk Set of GPIO lines to reserve. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the lines were properly reserved, -1 on failure. + */ +int gpiod_line_request_bulk_input_flags(struct gpiod_line_bulk *bulk, + const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Reserve a set of GPIO lines, set the direction to output. + * @param bulk Set of GPIO lines to reserve. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @param default_vals Initial line values. + * @return 0 if the lines were properly reserved, -1 on failure. + */ +int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk, + const char *consumer, int flags, + const int *default_vals) GPIOD_API; + +/** + * @brief Request rising edge event notifications on a set of lines. + * @param bulk Set of GPIO lines to request. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_bulk_rising_edge_events_flags( + struct gpiod_line_bulk *bulk, + const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Request falling edge event notifications on a set of lines. + * @param bulk Set of GPIO lines to request. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_bulk_falling_edge_events_flags( + struct gpiod_line_bulk *bulk, + const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Request all event type notifications on a set of lines. + * @param bulk Set of GPIO lines to request. + * @param consumer Name of the consumer. + * @param flags Additional request flags. + * @return 0 if the operation succeeds, -1 on failure. + */ +int gpiod_line_request_bulk_both_edges_events_flags( + struct gpiod_line_bulk *bulk, + const char *consumer, + int flags) GPIOD_API; + +/** + * @brief Release a previously reserved line. + * @param line GPIO line object. + */ +void gpiod_line_release(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Release a set of previously reserved lines. + * @param bulk Set of GPIO lines to release. + * + * If the lines were not previously requested together, the behavior is + * undefined. + */ +void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk) GPIOD_API; + +/** + * @brief Check if the calling user has ownership of this line. + * @param line GPIO line object. + * @return True if given line was requested, false otherwise. + */ +bool gpiod_line_is_requested(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the calling user has neither requested ownership of this + * line nor configured any event notifications. + * @param line GPIO line object. + * @return True if given line is free, false otherwise. + */ +bool gpiod_line_is_free(struct gpiod_line *line) GPIOD_API; + +/** + * @} + * + * @defgroup line_value Reading & setting line values + * @{ + * + * Functions allowing to read and set GPIO line values for single lines and + * in bulk. + */ + +/** + * @brief Read current value of a single GPIO line. + * @param line GPIO line object. + * @return 0 or 1 if the operation succeeds. On error this routine returns -1 + * and sets the last error number. + */ +int gpiod_line_get_value(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read current values of a set of GPIO lines. + * @param bulk Set of GPIO lines to reserve. + * @param values An array big enough to hold line_bulk->num_lines values. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * If succeeds, this routine fills the values array with a set of values in + * the same order, the lines are added to line_bulk. If the lines were not + * previously requested together, the behavior is undefined. + */ +int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, + int *values) GPIOD_API; + +/** + * @brief Set the value of a single GPIO line. + * @param line GPIO line object. + * @param value New value. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API; + +/** + * @brief Set the values of a set of GPIO lines. + * @param bulk Set of GPIO lines to reserve. + * @param values An array holding line_bulk->num_lines new values for lines. + * A NULL pointer is interpreted as a logical low for all lines. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * If the lines were not previously requested together, the behavior is + * undefined. + */ +int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, + const int *values) GPIOD_API; + +/** + * @} + * + * @defgroup line_config Setting line configuration + * @{ + * + * Functions allowing modification of config options of GPIO lines requested + * from user-space. + */ + +/** + * @brief Update the configuration of a single GPIO line. + * @param line GPIO line object. + * @param direction Updated direction which may be one of + * GPIOD_LINE_REQUEST_DIRECTION_AS_IS, + * GPIOD_LINE_REQUEST_DIRECTION_INPUT, or + * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. + * @param flags Replacement flags. + * @param value The new output value for the line when direction is + * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_set_config(struct gpiod_line *line, int direction, + int flags, int value) GPIOD_API; + +/** + * @brief Update the configuration of a set of GPIO lines. + * @param bulk Set of GPIO lines. + * @param direction Updated direction which may be one of + * GPIOD_LINE_REQUEST_DIRECTION_AS_IS, + * GPIOD_LINE_REQUEST_DIRECTION_INPUT, or + * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. + * @param flags Replacement flags. + * @param values An array holding line_bulk->num_lines new logical values + * for lines when direction is + * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. + * A NULL pointer is interpreted as a logical low for all lines. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * If the lines were not previously requested together, the behavior is + * undefined. + */ +int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk, + int direction, int flags, + const int *values) GPIOD_API; + + +/** + * @brief Update the configuration flags of a single GPIO line. + * @param line GPIO line object. + * @param flags Replacement flags. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_set_flags(struct gpiod_line *line, int flags) GPIOD_API; + +/** + * @brief Update the configuration flags of a set of GPIO lines. + * @param bulk Set of GPIO lines. + * @param flags Replacement flags. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * If the lines were not previously requested together, the behavior is + * undefined. + */ +int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk, + int flags) GPIOD_API; + +/** + * @brief Set the direction of a single GPIO line to input. + * @param line GPIO line object. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_set_direction_input(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Set the direction of a set of GPIO lines to input. + * @param bulk Set of GPIO lines. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * If the lines were not previously requested together, the behavior is + * undefined. + */ +int +gpiod_line_set_direction_input_bulk(struct gpiod_line_bulk *bulk) GPIOD_API; + +/** + * @brief Set the direction of a single GPIO line to output. + * @param line GPIO line object. + * @param value The logical value output on the line. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_set_direction_output(struct gpiod_line *line, + int value) GPIOD_API; + +/** + * @brief Set the direction of a set of GPIO lines to output. + * @param bulk Set of GPIO lines. + * @param values An array holding line_bulk->num_lines new logical values + * for lines. A NULL pointer is interpreted as a logical low + * for all lines. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * If the lines were not previously requested together, the behavior is + * undefined. + */ +int gpiod_line_set_direction_output_bulk(struct gpiod_line_bulk *bulk, + const int *values) GPIOD_API; + +/** + * @} + * + * @defgroup line_event Line events handling + * @{ + * + * Structures and functions allowing to poll lines for events and read them, + * both for individual lines as well as in bulk. Also contains functions for + * retrieving the associated file descriptors and operate on them for easy + * integration with standard unix interfaces. + */ + +/** + * @brief Event types. + */ +enum { + GPIOD_LINE_EVENT_RISING_EDGE = 1, + /**< Rising edge event. */ + GPIOD_LINE_EVENT_FALLING_EDGE, + /**< Falling edge event. */ +}; + +/** + * @brief Structure holding event info. + */ +struct gpiod_line_event { + struct timespec ts; + /**< Best estimate of time of event occurrence. */ + int event_type; + /**< Type of the event that occurred. */ +}; + +/** + * @brief Wait for an event on a single line. + * @param line GPIO line object. + * @param timeout Wait time limit. + * @return 0 if wait timed out, -1 if an error occurred, 1 if an event + * occurred. + */ +int gpiod_line_event_wait(struct gpiod_line *line, + const struct timespec *timeout) GPIOD_API; + +/** + * @brief Wait for events on a set of lines. + * @param bulk Set of GPIO lines to monitor. + * @param timeout Wait time limit. + * @param event_bulk Bulk object in which to store the line handles on which + * events occurred. Can be NULL. + * @return 0 if wait timed out, -1 if an error occurred, 1 if at least one + * event occurred. + */ +int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk, + const struct timespec *timeout, + struct gpiod_line_bulk *event_bulk) GPIOD_API; + +/** + * @brief Read next pending event from the GPIO line. + * @param line GPIO line object. + * @param event Buffer to which the event data will be copied. + * @return 0 if the event was read correctly, -1 on error. + * @note This function will block if no event was queued for this line. + */ +int gpiod_line_event_read(struct gpiod_line *line, + struct gpiod_line_event *event) GPIOD_API; + +/** + * @brief Read up to a certain number of events from the GPIO line. + * @param line GPIO line object. + * @param events Buffer to which the event data will be copied. Must hold at + * least the amount of events specified in num_events. + * @param num_events Specifies how many events can be stored in the buffer. + * @return On success returns the number of events stored in the buffer, on + * failure -1 is returned. + */ +int gpiod_line_event_read_multiple(struct gpiod_line *line, + struct gpiod_line_event *events, + unsigned int num_events) GPIOD_API; + +/** + * @brief Get the event file descriptor. + * @param line GPIO line object. + * @return Number of the event file descriptor or -1 if the user tries to + * retrieve the descriptor from a line that wasn't configured for + * event monitoring. + * + * Users may want to poll the event file descriptor on their own. This routine + * allows to access it. + */ +int gpiod_line_event_get_fd(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the last GPIO event directly from a file descriptor. + * @param fd File descriptor. + * @param event Buffer in which the event data will be stored. + * @return 0 if the event was read correctly, -1 on error. + * + * Users who directly poll the file descriptor for incoming events can also + * directly read the event data from it using this routine. This function + * translates the kernel representation of the event to the libgpiod format. + */ +int gpiod_line_event_read_fd(int fd, struct gpiod_line_event *event) GPIOD_API; + +/** + * @brief Read up to a certain number of events directly from a file descriptor. + * @param fd File descriptor. + * @param events Buffer to which the event data will be copied. Must hold at + * least the amount of events specified in num_events. + * @param num_events Specifies how many events can be stored in the buffer. + * @return On success returns the number of events stored in the buffer, on + * failure -1 is returned. + */ +int gpiod_line_event_read_fd_multiple(int fd, struct gpiod_line_event *events, + unsigned int num_events) GPIOD_API; + +/** + * @} + * + * @defgroup line_misc Misc line functions + * @{ + * + * Functions that didn't fit anywhere else. + */ + +/** + * @brief Get a GPIO line handle by GPIO chip description and offset. + * @param device String describing the gpiochip. + * @param offset The offset of the GPIO line. + * @return GPIO line handle or NULL if an error occurred. + * + * This routine provides a shorter alternative to calling + * ::gpiod_chip_open_lookup and ::gpiod_chip_get_line. + * + * If this function succeeds, the caller is responsible for closing the + * associated GPIO chip. + */ +struct gpiod_line * +gpiod_line_get(const char *device, unsigned int offset) GPIOD_API; + +/** + * @brief Find a GPIO line by its name. + * @param name Name of the GPIO line. + * @return Returns the GPIO line handle if the line exists in the system or + * NULL if it couldn't be located or an error occurred. + * @attention GPIO lines are not unique in the linux kernel, neither globally + * nor within a single chip. This function finds the first line with + * given name. + * + * If this routine succeeds, the user must manually close the GPIO chip owning + * this line to avoid memory leaks. If the line could not be found, this + * functions sets errno to ENOENT. + */ +struct gpiod_line *gpiod_line_find(const char *name) GPIOD_API; + +/** + * @brief Close a GPIO chip owning this line and release all resources. + * @param line GPIO line object + * + * After this function returns, the line must no longer be used. + */ +void gpiod_line_close_chip(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Get the handle to the GPIO chip controlling this line. + * @param line The GPIO line object. + * @return Pointer to the GPIO chip handle controlling this line. + */ +struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line) GPIOD_API; + +/** + * @} + * + * @} + * + * @defgroup iterators Iterators for GPIO chips and lines + * @{ + * + * These functions and data structures allow easy iterating over GPIO + * chips and lines. + */ + +/** + * @brief Create a new gpiochip iterator. + * @return Pointer to a new chip iterator object or NULL if an error occurred. + * + * Internally this routine scans the /dev/ directory for GPIO chip device + * files, opens them and stores their the handles until ::gpiod_chip_iter_free + * or ::gpiod_chip_iter_free_noclose is called. + */ +struct gpiod_chip_iter *gpiod_chip_iter_new(void) GPIOD_API; + +/** + * @brief Release all resources allocated for the gpiochip iterator and close + * the most recently opened gpiochip (if any). + * @param iter The gpiochip iterator object. + */ +void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Release all resources allocated for the gpiochip iterator but + * don't close the most recently opened gpiochip (if any). + * @param iter The gpiochip iterator object. + * + * Users may want to break the loop when iterating over gpiochips and keep + * the most recently opened chip active while freeing the iterator data. + * This routine enables that. + */ +void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Get the next gpiochip handle. + * @param iter The gpiochip iterator object. + * @return Pointer to the next open gpiochip handle or NULL if no more chips + * are present in the system. + * @note The previous chip handle will be closed using ::gpiod_chip_iter_free. + */ +struct gpiod_chip * +gpiod_chip_iter_next(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Get the next gpiochip handle without closing the previous one. + * @param iter The gpiochip iterator object. + * @return Pointer to the next open gpiochip handle or NULL if no more chips + * are present in the system. + * @note This function works just like ::gpiod_chip_iter_next but doesn't + * close the most recently opened chip handle. + */ +struct gpiod_chip * +gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Iterate over all GPIO chips present in the system. + * @param iter An initialized GPIO chip iterator. + * @param chip Pointer to a GPIO chip handle. On each iteration the newly + * opened chip handle is assigned to this argument. + * + * The user must not close the GPIO chip manually - instead the previous chip + * handle is closed automatically on the next iteration. The last chip to be + * opened is closed internally by ::gpiod_chip_iter_free. + */ +#define gpiod_foreach_chip(iter, chip) \ + for ((chip) = gpiod_chip_iter_next(iter); \ + (chip); \ + (chip) = gpiod_chip_iter_next(iter)) + +/** + * @brief Iterate over all chips present in the system without closing them. + * @param iter An initialized GPIO chip iterator. + * @param chip Pointer to a GPIO chip handle. On each iteration the newly + * opened chip handle is assigned to this argument. + * + * The user must close all the GPIO chips manually after use, until then, the + * chips remain open. Free the iterator by calling + * ::gpiod_chip_iter_free_noclose to avoid closing the last chip automatically. + */ +#define gpiod_foreach_chip_noclose(iter, chip) \ + for ((chip) = gpiod_chip_iter_next_noclose(iter); \ + (chip); \ + (chip) = gpiod_chip_iter_next_noclose(iter)) + +/** + * @brief Create a new line iterator. + * @param chip Active gpiochip handle over the lines of which we want + * to iterate. + * @return New line iterator or NULL if an error occurred. + */ +struct gpiod_line_iter * +gpiod_line_iter_new(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Free all resources associated with a GPIO line iterator. + * @param iter Line iterator object. + */ +void gpiod_line_iter_free(struct gpiod_line_iter *iter) GPIOD_API; + +/** + * @brief Get the next GPIO line handle. + * @param iter The GPIO line iterator object. + * @return Pointer to the next GPIO line handle or NULL if there are no more + * lines left. + */ +struct gpiod_line * +gpiod_line_iter_next(struct gpiod_line_iter *iter) GPIOD_API; + +/** + * @brief Iterate over all GPIO lines of a single chip. + * @param iter An initialized GPIO line iterator. + * @param line Pointer to a GPIO line handle - on each iteration, the + * next GPIO line will be assigned to this argument. + */ +#define gpiod_foreach_line(iter, line) \ + for ((line) = gpiod_line_iter_next(iter); \ + (line); \ + (line) = gpiod_line_iter_next(iter)) + +/** + * @} + * + * @defgroup misc Stuff that didn't fit anywhere else + * @{ + * + * Various libgpiod-related functions. + */ + +/** + * @brief Get the API version of the library as a human-readable string. + * @return Human-readable string containing the library version. + */ +const char *gpiod_version_string(void) GPIOD_API; + +/** + * @} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __LIBGPIOD_GPIOD_H__ */ diff --git a/app_sdk/app_src/app/src/main/cpp/led_control.cpp b/app_sdk/app_src/app/src/main/cpp/led_control.cpp new file mode 100644 index 0000000..2be1b9f --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/led_control.cpp @@ -0,0 +1,160 @@ +#include <jni.h> +#include <string> +#include <android/log.h> +#include "include/gpiod.h" + +enum +{ + LED_R=0, + LED_Y, + LED_G, + LED_MAX, +}; + +enum +{ + ON=0, + OFF, +}; + +typedef struct led_gpio_s +{ + int idx; + int gpio; + const char *desc; + struct gpiod_line *line; +}led_gpio_t; + +led_gpio_t leds[LED_MAX] = + { + {LED_R, 18, "red", NULL}, + {LED_Y, 22, "yellow", NULL}, + {LED_G, 20, "green", NULL}, + }; + +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_LedControl_ledOpen(JNIEnv *env, jclass clazz) { + // TODO: implement ledOpen() + struct gpiod_line *line; + struct gpiod_chip *chip; + const char *chipname = "gpiochip0"; + int ret; + int i, j; + int rv = 0; + + chip = gpiod_chip_open_by_name(chipname); + if( !chip ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "gpiod open '%s' failed:%s\n", chipname, strerror(errno)); + return -1; + } + + for( i=0; i<LED_MAX; i++ ) + { + // 获取句柄 + leds[i].line = gpiod_chip_get_line(chip, leds[i].gpio); + if( !leds[i].line ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "Get line[%d] failed:%s\n", leds[i].gpio, strerror(errno)); + gpiod_line_release(leds[i].line); + gpiod_chip_close(chip); + return -2; + } + + // 判断该GPIO接口是否被占用 + ret = gpiod_line_is_used(leds[i].line); + if( ret ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "line[%d] is used!\n", leds[i].gpio); + gpiod_chip_close(chip); + return -3; + } + + // 设置该接口为输出模式 + ret = gpiod_line_request_output(leds[i].line, leds[i].desc, 0); + if( ret < 0 ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "set line[%d] output failed:%s\n", leds[i].gpio, strerror(errno)); + gpiod_line_release(leds[i].line); + gpiod_chip_close(chip); + return -4; + } + } +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_LedControl_ledCtrl(JNIEnv *env, jclass clazz, jint which, jint status) { + // TODO: implement ledCtrl() + if( which<0 || which>=LED_MAX ) + { + return -1; + } + + struct gpiod_chip *chip; + const char *chipname = "gpiochip0"; + int ret; + int rv = 0; + + chip = gpiod_chip_open_by_name(chipname); + if( !chip ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "gpiod open '%s' failed:%s\n", chipname, strerror(errno)); + return -1; + } + + // 获取句柄 + leds[which].line = gpiod_chip_get_line(chip, leds[which].gpio); + if( !leds[which].line ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "Get line[%d] failed:%s\n", leds[which].gpio, strerror(errno)); + gpiod_line_release(leds[which].line); + gpiod_chip_close(chip); + return -2; + } + + // 判断该GPIO接口是否被占用 + ret = gpiod_line_is_used(leds[which].line); + if( ret ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "line[%d] is used!\n", leds[which].gpio); + gpiod_chip_close(chip); + return -3; + } + + // 设置该接口为输出模式 + ret = gpiod_line_request_output(leds[which].line, leds[which].desc, 0); + if( ret < 0 ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "set line[%d] output failed:%s\n", leds[which].gpio, strerror(errno)); + gpiod_line_release(leds[which].line); + gpiod_chip_close(chip); + return -4; + } + + __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "turn %s led GPIO#%d %s\n", leds[which].desc, leds[which].gpio, status?"off":"on"); + + if( ON == status ) + { + rv = gpiod_line_set_value(leds[which].line, 0); + if( rv < 0 ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "turn %s ON failed:%s\n", leds[which].desc, strerror(errno)); + return -5; + } + } + else + { + rv = gpiod_line_set_value(leds[which].line, 1); + if( rv < 0 ) + { + __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "turn %s OFF failed:%s\n", leds[which].desc, strerror(errno)); + return -5; + } + } + + gpiod_line_release(leds[which].line); + gpiod_chip_close(chip); + return 0; +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/cpp/native-lib.cpp b/app_sdk/app_src/app/src/main/cpp/native-lib.cpp new file mode 100644 index 0000000..1af95fe --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,51 @@ +#include <jni.h> +#include <string> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <android/log.h> +#define TAG "BuzzerApp" +#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) + +static char pwm_path[100]; + +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_BuzzerControl_openPwm(JNIEnv *env, jclass clazz, jstring id) { + // TODO: implement openPwm() + char *id_native; + char temp[100]; + int fd; + id_native = (char *)env->GetStringUTFChars(id, 0); + memset(pwm_path, 0, sizeof (pwm_path)); + snprintf(pwm_path, sizeof (pwm_path), "/sys/class/pwm/pwmchip%s/pwm0", id_native); + + memset(temp, 0, sizeof (temp)); + if(access(pwm_path, F_OK)) + { + snprintf(temp, sizeof (temp), "/sys/class/pwm/pwmchip%s/export", id_native); + fd = open(temp, O_WRONLY); + if(fd < 0) + { + LOGE("open %s error:%s\n", temp, strerror(errno)); + return -1; + } + + if(1 != write(fd, "0", 1)) + { + LOGE("Write '0' to pwmchip%s/export error\n", id_native); + close(fd); + return -2; + } + close(fd); + } + env->ReleaseStringUTFChars(id, id_native); + return 0; +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/cpp/rs485_control.cpp b/app_sdk/app_src/app/src/main/cpp/rs485_control.cpp new file mode 100644 index 0000000..cec9b7b --- /dev/null +++ b/app_sdk/app_src/app/src/main/cpp/rs485_control.cpp @@ -0,0 +1,516 @@ +#include <jni.h> +#include <string> +#include <termios.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <android/log.h> +#include "include/gpiod.h" + +#define TAG "SerialApp" +#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) + +typedef struct comport_s +{ + char devname[12]; + unsigned int databit, parity, stopbit, flowctrl; + long baudrate; + + int fd; + int frag_size; +}comport_t; + +comport_t *comport; + +// 将int类型的波特率转换成可以识别的波特率 +static speed_t getBaudRate(int baudRate){ + switch (baudRate) { + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } +} + +extern "C" +JNIEXPORT int JNICALL +Java_com_example_serial_RS485Control_openSerialPort(JNIEnv *env, jclass clazz, jstring path, + jlong baud_rate, jint data_bits, jint parity, + jint stop_bits, jint flow_control) { + // TODO: implement openSerialPort() + speed_t speed; + struct termios old_cfg, new_cfg; + int old_flags; + long temp; + + comport = (comport_t *) malloc(sizeof(comport_t)); + if (comport == NULL) { + LOGE("Failed to allocate memory for comport structure."); + return -1; + } + memset(comport, 0, sizeof(comport)); + +// mSend_len = max_len; + + // 检查波特率是否合法 + { + speed = getBaudRate(baud_rate); + if (speed == -1) + { + LOGE("Invalid buad rate"); + return -1; + } + } + + // 打开串口设备 + { + jboolean iscopy; + // 获取String字符串path中的UTF8编码,并将其转换成C中的字符串 + const char* devname = (*env).GetStringUTFChars(path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", devname, O_RDWR); + strncpy(comport->devname, devname, 12); + comport->baudrate = baud_rate; + comport->fd = -1; + comport->frag_size = 128; + comport->databit = data_bits; + comport->parity = parity; + comport->flowctrl = flow_control; + comport->stopbit = stop_bits; + + if( !strstr(comport->devname, "tty") ) + { + LOGE("Open Not a tty device \" %s\" \n", comport->devname); + return -2; + } + + comport->fd = open(comport->devname, O_RDWR); + if ( comport->fd < 0 ) + { + if( strstr(strerror(errno), "Permission denied") != nullptr ) + return 1; + LOGE("open port failed:%s\n", strerror(errno)); + return -3; + } + LOGD("Open device %s", comport->devname); + // 释放JNI字符串的UTF8编码 + (*env).ReleaseStringUTFChars(path, devname); + + if( (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK))) + { + if(-1 == tcflush(comport->fd, TCIOFLUSH)) + { + LOGE("open port failed:%s\n", strerror(errno)); + return -4; + } + } + else + { + LOGE("open port failed:%s\n", strerror(errno)); + return -5; + } + + if ( 0 != tcgetattr(comport->fd, &old_cfg)) + { + LOGE("open port failed:%s\n", strerror(errno)); + return -6; + } + } + + { + LOGD("starting configuring serial port"); + + old_cfg.c_cflag &= ~CSIZE; + old_cfg.c_lflag &= ~ICANON; + old_cfg.c_lflag &= ~ECHO; + old_cfg.c_lflag &= ~ECHOE; + old_cfg.c_lflag &= ~ECHONL; + old_cfg.c_lflag &= ~ISIG; + old_cfg.c_iflag &= ~(IXON | IXOFF | IXANY); + old_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); + old_cfg.c_oflag &= ~OPOST; + old_cfg.c_oflag &= ~ONLCR; + old_cfg.c_cflag |= CREAD | CLOCAL; + + switch (comport->databit) + { + case 7: + old_cfg.c_cflag |= CS7; + break; + case 6: + old_cfg.c_cflag |= CS6; + break; + case 5: + old_cfg.c_cflag |= CS5; + break; + case 8: + old_cfg.c_cflag |= CS8; + break; + } + + switch(comport->parity) + { + case 1: + old_cfg.c_cflag |= (PARENB | PARODD); + old_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 2: + old_cfg.c_cflag |= PARENB; + old_cfg.c_cflag &= ~PARODD; + old_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0: + old_cfg.c_cflag &= ~PARENB; + break; + } + + if(1 != comport->stopbit) + { + old_cfg.c_cflag |= CSTOPB; + } + else + { + old_cfg.c_cflag &= ~CSTOPB; + } + + // set flow control + // 1: software control; 2:hardware control; 0: none + switch(comport->flowctrl) + { + case 1: + old_cfg.c_cflag &= ~(CRTSCTS); + old_cfg.c_iflag |= (IXON | IXOFF); + break; + case 2: + old_cfg.c_cflag |= CRTSCTS; + old_cfg.c_iflag &= ~(IXON | IXOFF); + break; + case 0: + old_cfg.c_cflag &= ~(CRTSCTS); + break; + } + + + // set baudRate + cfsetispeed(&old_cfg, speed); + cfsetospeed(&old_cfg, speed); + + new_cfg.c_cc[VMIN] = 10; + new_cfg.c_cc[VTIME] = 0; + + + // 将设置的串口参数应用到串口上,TCSANOW表示立即生效,(TCSADRAIN:在所有输出都被传输后生效;TCSAFLUSH:在所有输出都被传输后生效,同时丢弃所有未读取的输入) + if(tcsetattr(comport->fd, TCSANOW, &old_cfg)) + { + LOGE("tcsetattr() failed:%s", strerror(errno)); + close(comport->fd); + return -1; + } + LOGD("Connected device \" %s \" successfully\n", comport->devname); + LOGD("port:%s, databit:%d, stopbit:%d, parity:%d, flowctl:%d", comport->devname, comport->databit, comport->stopbit, comport->parity, comport->flowctrl); + } + return 0; +} +extern "C" +JNIEXPORT int JNICALL +Java_com_example_serial_RS485Control_closeSerialPort(JNIEnv *env, jclass clazz) { + // TODO: implement closeSerialPort() + if ( !comport ) + { + LOGE("%s() get invalid input arguments.\n", __FUNCTION__ ); + return -1; + } + + if (comport->fd >= 0) + { + close(comport->fd); + LOGD("close device \" %s \" successfully\n", comport->devname); + } + comport->fd = -1; + free(comport); + comport = NULL; + return 0; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_RS485Control_sendToPort(JNIEnv *env, jclass clazz, jstring msg, jint len) { + // TODO: implement sendToPort() + // msg: 表示发送的数据;len: 发送的长度 + int rv; + char *ptr, left_bytes; + int send = 0; + const char *sendMsg = env->GetStringUTFChars(msg, 0); + if ( sendMsg == nullptr ) + { + return -1; + } + + if( !comport || !msg || len <= 0 ) + { + LOGE("Invalid parameter.\n"); + return -1; + } + if (comport->fd == -1) + { + LOGE("Serail not connected.\n"); + return -2; + } + + left_bytes = len; + ptr = (char *)sendMsg; + + while( left_bytes > 0 ) + { + /* Large data, then slice them to frag and send */ + send = left_bytes>comport->frag_size ? comport->frag_size : left_bytes; + + rv = write(comport->fd, ptr, send); + if( rv<0 ) + { + return -4; + } + + left_bytes -= rv; + ptr += rv; + } + LOGD("send %s successfully\n", sendMsg); + + env->ReleaseStringUTFChars(msg, sendMsg); + return rv; +} + + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_example_serial_RS485Control_recvFromPort(JNIEnv *env, jclass clazz, jint len, jint timeout) { + int rv = 0; + int iRet; + fd_set rdfds, exfds; + struct timeval stTime; + + jstring result; + char nativeMsg[len]; + memset(nativeMsg, 0, len); + + if( !comport || len <= 0 ) + { + LOGE("Invalid parameter.\n"); + return nullptr; + } + if (comport->fd == -1) + { + LOGE("Serail not connected.\n"); + return nullptr; + } + + FD_ZERO(&rdfds); + FD_ZERO(&exfds); + FD_SET(comport->fd, &rdfds); + FD_SET(comport->fd, &exfds); + + if (0xFFFFFFFF != timeout) + { + stTime.tv_sec = (time_t) (timeout / 1000); + stTime.tv_usec = (long)(1000 * (timeout % 1000)); + + iRet = select(comport->fd + 1, &rdfds, 0, &exfds, &stTime); + if (0 == iRet) + { + return nullptr; + } + else if (0 < iRet) + { + if (0 != FD_ISSET(comport->fd, &exfds)) + { + LOGE("Error checking recv status.\n"); + return nullptr; + } + + if (0 == FD_ISSET(comport->fd, &rdfds)) + { + LOGE("No incoming data.\n"); + return nullptr; + } + } + else + { + if (EINTR == errno) + { + LOGE("catch interrupt signal.\n"); +// rv = 0; + } + else + { + LOGE("Check recv status failure.\n"); +// rv = -7; + } + return nullptr; + } + } + + usleep(10000); /* sleep for 10ms for data incoming */ + + // Get data from Com port + iRet = read(comport->fd, nativeMsg, len); + if (0 > iRet) + { + if (EINTR == errno) + rv = 0; // Interrupted signal catched + else + rv = -3; // Failed to read comport + + return nullptr; + } + LOGD("Receive {%s} successfully\n", nativeMsg); + + return env->NewStringUTF(nativeMsg); +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_example_serial_RS485Control_saveToFile(JNIEnv *env, jclass clazz, jstring msg, jint len, + jstring file_name) { + // TODO: implement saveToFile() + int mFd = -1; + int rv; + mFd = open("dev/log.txt", O_CREAT | O_RDWR | O_APPEND, 0666); + rv = write(mFd, msg, len); + if (rv < 0) { + LOGE("Save data to file failed:%s\n" , strerror(errno)); + return nullptr; + } + LOGD("save received data into file successfully!\n"); + char buf[1024]; + rv = read(mFd, buf, len); + if (rv < 0) { + LOGE("Read data failed:%s\n" , strerror(errno)); + return nullptr; + } + strcat(reinterpret_cast<char *>(buf), "file"); + close(mFd); + return reinterpret_cast<jstring>(buf); +} + +// RS485是半双工通信的,由GPIO的高低电平来控制其是接收状态还是发送状态 +// 0: 接收(默认);1:发送 +// 切换状态成功返回0,失败返回-1 +extern "C" +JNIEXPORT jint JNICALL +Java_com_example_serial_RS485Control_changeState(JNIEnv *env, jclass clazz, jint state) { + // TODO: implement changeState() + struct gpiod_chip *chip; + struct gpiod_line *line; + const char *chipname = "gpiochip4"; + int rv = 0; + int gpio = 26; + + + chip = gpiod_chip_open_by_name(chipname); + if( !chip ) + { + LOGE("Gpio open '%s' failed:%s\n", chipname, strerror(errno)); + return -1; + } + + line = gpiod_chip_get_line(chip, gpio); + if ( !line ) + { + LOGE("Get gpio[%d] failed:%s\n", gpio, strerror(errno)); + rv = -1; + goto Cleanup; + } + + rv = gpiod_line_is_used(line); + if( rv ) + { + LOGE("GPIO[%d] is used\n", gpio); + rv = -1; + goto Cleanup; + } + + rv = gpiod_line_request_output(line, "rs485", 0); + if ( rv < 0 ) + { + LOGE("Set GPIO[%d] as output failed:%s\n", gpio, strerror(errno)); + rv = -1; + goto Cleanup; + } + + rv = gpiod_line_set_value(line, state); + if ( rv < 0 ) + { + LOGE("Set GPIO[%d]'s value failed:%s\n", gpio, strerror(errno)); + rv = -1; + goto Cleanup; + } + LOGD("Set GPIO[%d]'s value as %d successfully\n", gpio, state); + rv = 0; + + Cleanup: + gpiod_line_release(line); + gpiod_chip_close(chip); + return rv; +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java new file mode 100644 index 0000000..2e5cb0c --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java @@ -0,0 +1,247 @@ +package com.example.serial; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; + +import android.annotation.SuppressLint; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Bundle; +import android.text.method.ScrollingMovementMethod; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import com.example.serial.databinding.ActivityMainBinding; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + + +public class AdcActivity extends AppCompatActivity { + + // 与串口参数相关的组件 + private ActivityMainBinding binding; + private Spinner mAdcSpBaud; + private Spinner mAdcSpSerial; + private Spinner mAdcSpData; + private Spinner mAdcSpStop; + private Spinner mAdcSpParity; + private Spinner mAdcSpFlow; + private Spinner mAdcSpOperate; + private Spinner mAdcSpChannel; + + private Button mAdcBtnSend; + private Button mAdcBtnOpen; + private Button mAdcBtnClose; + private Button mAdcBtnRecv; + private TextView mAdcRecv; + + // 设置串口的参数 + private String serial = "/dev/ttyS6"; + private long baudRate; + private int dataBits; + private double stopBits; + private int parity; + private int flowControl; // 0:None, 1:RTS/CTS, 2:XON/XOFF + private String operate; // 电流or电压 + private String channel; + + // 获取已经设定的值 + String[] serialArray; + String[] baudArray; + String[] dataArray; + String[] stopArray; + String[] parityArray; + String[] flowArray; + String[] operateArray; + String[] channelArray; + private AdcControl adcControl; + + private int len = 16; + private int timmout = 1000; + + boolean turnFlag = false; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_adc); + + adcControl = new AdcControl(); + // 找到控件 + mAdcSpSerial = findViewById(R.id.adc_sp_serial); + mAdcSpBaud = findViewById(R.id.adc_sp_baud); + mAdcSpData = findViewById(R.id.adc_sp_data); + mAdcSpStop = findViewById(R.id.adc_sp_stop); + mAdcSpParity = findViewById(R.id.adc_sp_parity); + mAdcSpFlow = findViewById(R.id.adc_sp_flow); + mAdcSpOperate = findViewById(R.id.adc_sp_operate); + mAdcSpChannel = findViewById(R.id.adc_sp_channel); + + mAdcBtnSend = findViewById(R.id.adc_btn_send); + mAdcBtnOpen = findViewById(R.id.adc_btn_open); + mAdcBtnClose = findViewById(R.id.adc_btn_close); + mAdcBtnRecv = findViewById(R.id.adc_btn_recv); + mAdcRecv = findViewById(R.id.adc_txt_rev); + + // 设置并监听下拉列表 + setSpinner(mAdcSpSerial); + setSpinner(mAdcSpBaud); + setSpinner(mAdcSpData); + setSpinner(mAdcSpStop); + setSpinner(mAdcSpParity); + setSpinner(mAdcSpFlow); + setSpinner(mAdcSpOperate); + setSpinner(mAdcSpChannel); + + // 监听所有的button + setListener(); + mAdcRecv.setMovementMethod(ScrollingMovementMethod.getInstance()); + + } + + @Override + protected void onDestroy() { + adcControl.closeComport(); + super.onDestroy(); + } + + private void setSpinner(Spinner spinner){ + ArrayAdapter<CharSequence> adapter; + int id = spinner.getId(); + + if (id == R.id.adc_sp_serial){ + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.adc_serial_name, android.R.layout.simple_spinner_item); + }else if (id == R.id.adc_sp_baud) { + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.baud_rate, android.R.layout.simple_spinner_item); + } else if (id == R.id.adc_sp_data) { + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.data_bits, android.R.layout.simple_spinner_item); + } else if (id == R.id.adc_sp_stop) { + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.stop_bits, android.R.layout.simple_spinner_item); + } else if (id == R.id.adc_sp_parity) { + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.parity, android.R.layout.simple_spinner_item); + } else if (id == R.id.adc_sp_flow) { + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.flow_control, android.R.layout.simple_spinner_item); + } else if (id == R.id.adc_sp_operate) { + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.adc_operate, android.R.layout.simple_spinner_item); + } else if (id == R.id.adc_sp_channel){ + adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.adc_channel, android.R.layout.simple_spinner_item); + } else { + throw new IllegalStateException("Unexpected value: " + spinner.getId()); + } + adapter.setDropDownViewResource(android.R.layout.simple_spinner_item); + spinner.setAdapter(adapter); + + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + int spinnerId = spinner.getId(); + + if (spinnerId == R.id.adc_sp_serial) { + serialArray = getResources().getStringArray(R.array.adc_serial_name); + serial = serialArray[position]; + } else if (spinnerId == R.id.adc_sp_baud) { + baudArray = getResources().getStringArray(R.array.baud_rate); + baudRate = Integer.parseInt(baudArray[position]); + } else if (spinnerId == R.id.adc_sp_data) { + dataArray = getResources().getStringArray(R.array.data_bits); + dataBits = Integer.parseInt(dataArray[position]); + } else if (spinnerId == R.id.adc_sp_stop) { + stopArray = getResources().getStringArray(R.array.stop_bits); + stopBits = Double.parseDouble(stopArray[position]); + } else if (spinnerId == R.id.adc_sp_parity) { + parityArray = getResources().getStringArray(R.array.parity); + if (parityArray[position].equals("偶校验")) + parity = 2; + else if (parityArray[position].equals("奇校验")) + parity = 1; + else if (parityArray[position].equals("None")) + parity = 0; + } else if (spinnerId == R.id.adc_sp_flow) { + flowArray = getResources().getStringArray(R.array.flow_control); + flowControl = position; + } else if (spinnerId == R.id.adc_sp_operate) { + operateArray = getResources().getStringArray(R.array.adc_operate); + operate = operateArray[position]; + } else if (spinnerId == R.id.adc_sp_channel) { + channelArray = getResources().getStringArray(R.array.adc_channel); + channel = channelArray[position]; + } else { + throw new IllegalStateException("Unexpected value: " + spinner.getId()); + } + } + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }); + } + + private void setListener(){ + OnClick onClick = new OnClick(); + mAdcBtnSend.setOnClickListener(onClick); + mAdcBtnOpen.setOnClickListener(onClick); + mAdcBtnClose.setOnClickListener(onClick); + mAdcBtnRecv.setOnClickListener(onClick); + } + private class OnClick implements View.OnClickListener{ + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.adc_btn_open) { + if (turnFlag) { + Toast.makeText(getApplicationContext(), "串口已打开, 不能重复打开", Toast.LENGTH_SHORT).show(); + }else { + int rv = adcControl.openComport(serial, (int) baudRate, dataBits, parity, (int) stopBits, flowControl); + if(rv == 1){ + Toast.makeText(getApplicationContext(), "请添加"+serial+"的权限", Toast.LENGTH_SHORT).show(); + }else if (rv < 0) { + Toast.makeText(getApplicationContext(), "打开串口失败", Toast.LENGTH_SHORT).show(); + } else { + turnFlag = true; + Toast.makeText(getApplicationContext(), "成功打开串口"+serial, Toast.LENGTH_SHORT).show(); + } + } + } else if (id == R.id.adc_btn_close){ + if (!turnFlag) { + Toast.makeText(getApplicationContext(), "串口已关闭,不能重复关闭", Toast.LENGTH_SHORT).show(); + }else { + // 关闭串口 + int rv = adcControl.closeComport(); + if (rv < 0) { + Toast.makeText(getApplicationContext(), "关闭串口失败", Toast.LENGTH_SHORT).show(); + } else { + turnFlag = false; + Toast.makeText(getApplicationContext(), "关闭串口", Toast.LENGTH_SHORT).show(); + } + } + }else if (id == R.id.adc_btn_send) { + int rv = adcControl.sendToPort(operate, channel); + if (rv < 0) { + Toast.makeText(getApplicationContext(), "发送数据失败!", Toast.LENGTH_SHORT).show(); + } + Toast.makeText(getApplicationContext(), "发送数据成功", Toast.LENGTH_SHORT).show(); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("GMT+8")); + String date = df.format(new Date()); + String buf = adcControl.recvFromPort(len, timmout); // 超时时间设置为 1000 ms + if (buf != null) { + String revData = date + "\t"+ "Received: " + buf; + mAdcRecv.append("\n" + revData); + mAdcRecv.invalidate(); + mAdcRecv.requestLayout(); + } + } else if (id == R.id.adc_btn_recv) { + mAdcRecv.setText(" "); + } + } + } +} diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java new file mode 100644 index 0000000..feca1c8 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java @@ -0,0 +1,12 @@ +package com.example.serial; + +public class AdcControl { + static { + System.loadLibrary("serial"); + } + public static native int openComport(String path, long baudRate, int dataBits, int parity, int stopBits, int flowControl); + public static native int closeComport(); + + public static native int sendToPort(String operate, String channel); + public static native String recvFromPort(int len, int timeout); +} diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java new file mode 100644 index 0000000..c1a61c3 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java @@ -0,0 +1,104 @@ +package com.example.serial; +import androidx.appcompat.app.AppCompatActivity; + +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +public class BuzzerActivity extends AppCompatActivity { + + private Button play; + private Button stop; + private EditText period; + private EditText duty_cycle; + + private String periodBuf; + private String dutyBuf; + private BuzzerControl buzzerControl; + private int rv; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_buzzer); + buzzerControl = new BuzzerControl(); + + play = findViewById(R.id.play); + period = findViewById(R.id.period); + duty_cycle = findViewById(R.id.duty_cycle); + stop = findViewById(R.id.stop); + + + play.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + periodBuf = period.getText().toString(); + dutyBuf = duty_cycle.getText().toString(); + + if(periodBuf.isEmpty() || dutyBuf.isEmpty()){ + periodBuf = period.getHint().toString(); + dutyBuf = duty_cycle.getHint().toString(); + period.setText(periodBuf); + duty_cycle.setText(dutyBuf); + } + // 打开pwmchip2/pwm0 + rv = buzzerControl.pwmOpen("2"); + if (rv == 1) { + // 表示缺少权限 + Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/export的权限", Toast.LENGTH_SHORT).show(); + }else if( rv < 0 ){ + Toast.makeText(BuzzerActivity.this, "打开PWM失败", Toast.LENGTH_SHORT).show(); + } else { + // 首先设置周期 + rv = buzzerControl.pwmConfig("period", periodBuf); + if (rv == 1) { + // 表示缺少权限 + Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/pwm0/period的权限", Toast.LENGTH_SHORT).show(); + } else if (rv == 2) { + Toast.makeText(BuzzerActivity.this, "周期的值无效,请重新输入", Toast.LENGTH_SHORT).show(); + }else if (rv < 0) { + Toast.makeText(BuzzerActivity.this, "设置周期失败", Toast.LENGTH_SHORT).show(); + }else { + // 其次设置占空比 + rv = buzzerControl.pwmConfig("duty_cycle", dutyBuf); + if (rv == 1) { + // 表示缺少权限 + Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/pwm0/duty_cycle的权限", Toast.LENGTH_SHORT).show(); + } else if (rv == 2) { + Toast.makeText(BuzzerActivity.this, "占空比的值无效,请重新输入", Toast.LENGTH_SHORT).show(); + } + if (rv < 0) { + Toast.makeText(BuzzerActivity.this, "设置占空比失败", Toast.LENGTH_SHORT).show(); + } else { + // 最后使能pwm + rv = buzzerControl.pwmConfig("enable", "1"); + if (rv == 1) { + // 表示缺少权限 + Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/pwm0/enable的权限", Toast.LENGTH_SHORT).show(); + } else if (rv < 0) { + Toast.makeText(BuzzerActivity.this, "使能PWM失败", Toast.LENGTH_SHORT).show(); + } else + Toast.makeText(BuzzerActivity.this, "打开PWM, 正在播放", Toast.LENGTH_SHORT).show(); + } + } + } + } + }); + + stop.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + rv = buzzerControl.pwmConfig("enable", "0"); + if( rv < 0 || rv == 1 || rv == 2) + Toast.makeText(BuzzerActivity.this, "禁止PWM失败", Toast.LENGTH_SHORT).show(); + else { + Toast.makeText(BuzzerActivity.this, "禁止PWM,暂停播放", Toast.LENGTH_SHORT).show(); + } + } + }); + + } +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java new file mode 100644 index 0000000..0c28db3 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java @@ -0,0 +1,9 @@ +package com.example.serial; + +public class BuzzerControl { + static{ + System.loadLibrary("serial"); + } + public static native int pwmOpen(String id); // 返回1表示缺少权限 + public static native int pwmConfig(String attr, String val); // 返回1表示缺少权限,2表示输入的值无效 +} diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java new file mode 100644 index 0000000..88be31b --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java @@ -0,0 +1,169 @@ +package com.example.serial; +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import android.os.Bundle; +import android.text.method.ScrollingMovementMethod; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class CanActivity extends AppCompatActivity { + private volatile boolean receiving = false; + private Thread receiveThread; + + private String data; // 存放发送的数据 + private String canId; + private String dlc; + private String canName; + private String status = "send"; + + private Button mBtnSend; + private Button mBtnRecv; + private Button mBtnSendClear; + private Button mBtnRecvClear; + private TextView mTvSend; + private TextView mTvRecv; + private EditText mEdId; + private EditText mEdDlc; + private EditText mEdData; + private Spinner mSpCan; + private Spinner mSpBit; + + private CanControl canControl = new CanControl(); + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_can); + + mBtnSend = findViewById(R.id.can_send); + mBtnRecv = findViewById(R.id.can_recv); + mBtnSendClear = findViewById(R.id.can_send_clear); + mBtnRecvClear = findViewById(R.id.can_recv_clear); + mTvSend = findViewById(R.id.can_tv_send); + mTvRecv = findViewById(R.id.can_tv_recv); + mEdId = findViewById(R.id.can_ed_id); + mEdDlc = findViewById(R.id.can_ed_dlc); + mEdData = findViewById(R.id.can_ed_data); + mSpCan = findViewById(R.id.sp_can); + + mTvSend.setMovementMethod(ScrollingMovementMethod.getInstance()); + mTvRecv.setMovementMethod(ScrollingMovementMethod.getInstance()); + + + ArrayAdapter<CharSequence> adapter_name; + adapter_name = ArrayAdapter.createFromResource(CanActivity.this, R.array.can_name, android.R.layout.simple_spinner_item); + adapter_name.setDropDownViewResource(android.R.layout.simple_spinner_item); + mSpCan.setAdapter(adapter_name); + mSpCan.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + canName = getResources().getStringArray(R.array.can_name)[position]; + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }); + + + setListener(); + + + } + + private void setListener() { + OnClick onClick = new OnClick(); + mBtnRecv.setOnClickListener(onClick); + mBtnRecvClear.setOnClickListener(onClick); + mBtnSend.setOnClickListener(onClick); + mBtnSendClear.setOnClickListener(onClick); + } + + private class OnClick implements View.OnClickListener { + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.can_send) { + status = "send"; + if (canName == null){ + Toast.makeText(CanActivity.this, "请选择CAN!", Toast.LENGTH_SHORT).show(); + } + canId = mEdId.getText().toString(); + dlc = mEdDlc.getText().toString(); + data = mEdData.getText().toString(); + if (Integer.parseInt(canId) > 0xFFF || Integer.parseInt(canId) < 0x000) { + Toast.makeText(CanActivity.this, "请输入正确的ID!", Toast.LENGTH_SHORT).show(); + }else if (Integer.parseInt(dlc) > 7 || Integer.parseInt(canId) < 0){ + Toast.makeText(CanActivity.this, "请输入正确的DLC!", Toast.LENGTH_SHORT).show(); + }else { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("GMT+8")); + String date = df.format(new Date()); + int rv = canControl.sendMessage(canId, dlc, data, canName); + if (rv < 0) { + Log.e("Send", "Send failed"); + Toast.makeText(CanActivity.this, "发送失败,检查网络是否连接!", Toast.LENGTH_SHORT).show(); + }else { + mTvSend.post(() -> { + String sendData = date + "\t" + "ID=" + canId + " DLC=" + dlc + " Data=" + data; + String currentText = mTvSend.getText().toString(); + String newText = currentText + "\n" + sendData; // 追加新数据 + mTvSend.append("\n" + sendData); + }); + } + } + } else if (id == R.id.can_send_clear) { + mTvSend.setText(""); + } else if (id == R.id.can_recv) { + if (canName == null){ + Toast.makeText(CanActivity.this, "请选择CAN!", Toast.LENGTH_SHORT).show(); + } + status = "recv"; + startReceiving(); + } else if (id == R.id.can_recv_clear) { + status = "stopRecv"; + mTvRecv.setText(""); + } + } + } + public void startReceiving() { + receiving = true; + Log.d("TAG", "startReceiving: "+status); + receiveThread = new Thread(() -> { + Log.d("TAG", "startReceiving: "+status); + while (status.equals("recv")) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("GMT+8")); + String date = df.format(new Date()); + String buf = canControl.receiveMessage(canName); + if (buf == null) { + Log.e("Recv", "Receive failed"); + } + Log.d("Recv", buf); + mTvRecv.post(() -> { + String revData = date + "\t" + buf; + String currentText = mTvRecv.getText().toString(); + String newText = currentText + "\n" + revData; // 追加新数据 + mTvRecv.setText(newText); + }); + try { + Thread.sleep(10); // 每 10 ms 读取一次数据 + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + receiveThread.start(); + } +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java new file mode 100644 index 0000000..8e51579 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java @@ -0,0 +1,9 @@ +package com.example.serial; + +public class CanControl { + static{ + System.loadLibrary("serial"); + } + public static native int sendMessage(String id, String dlc, String data, String ifname); + public static native String receiveMessage(String ifname); +} diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java new file mode 100644 index 0000000..9ea86b8 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java @@ -0,0 +1,111 @@ +package com.example.serial; +import static java.lang.Thread.sleep; + +import androidx.appcompat.app.AppCompatActivity; + +import android.graphics.PorterDuff; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +public class LedActivity extends AppCompatActivity { + private boolean redTag = false; + private boolean greenTag = false; + private boolean yellowTag = false; + private Button ledRed = null; + private Button ledGreen = null; + private Button ledYellow = null; + private ImageView imageRed = null; + private ImageView imageGreen = null; + private ImageView imageYellow = null; + private LedControl ledControl; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_led); + + ledRed = (Button)findViewById(R.id.red); + ledYellow = (Button)findViewById(R.id.yellow); + ledGreen = (Button)findViewById(R.id.green); + + imageRed = findViewById(R.id.imageRed); + imageYellow = findViewById(R.id.imageYellow); + imageGreen = findViewById(R.id.imageGreen); + + ledControl = new LedControl(); + ledControl.ledCtrl(1, 0); + try { + sleep(1); + ledControl.ledCtrl(1, 1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + + ledRed.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View v) { + redTag = !redTag; + if( redTag ) { + if(ledControl.ledCtrl(0, 0) < 0) + Toast.makeText(LedActivity.this, "点亮红灯失败", Toast.LENGTH_SHORT).show(); + else + imageRed.setColorFilter(getResources().getColor(android.R.color.holo_red_light), PorterDuff.Mode.SRC_IN); + }else{ + if(ledControl.ledCtrl(0, 1) < 0) + Toast.makeText(LedActivity.this, "关闭红灯失败", Toast.LENGTH_SHORT).show(); + else + imageRed.setColorFilter(getResources().getColor(android.R.color.darker_gray), PorterDuff.Mode.SRC_IN); + } + } + }); + ledYellow.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View v) { + yellowTag = !yellowTag; + if( yellowTag ) { + if( ledControl.ledCtrl(1, 0) < 0){ + Toast.makeText(LedActivity.this, "点亮黄灯失败!", Toast.LENGTH_SHORT).show(); + }else { + imageYellow.setColorFilter(getResources().getColor(android.R.color.holo_orange_light), PorterDuff.Mode.SRC_IN); + } + }else{ + if( ledControl.ledCtrl(1, 1) < 0 ){ + Toast.makeText(LedActivity.this, "关闭黄灯失败!", Toast.LENGTH_SHORT).show(); + }else + imageYellow.setColorFilter(getResources().getColor(android.R.color.darker_gray), PorterDuff.Mode.SRC_IN); + } + } + }); + + ledGreen.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View v) { + greenTag = !greenTag; + if( greenTag ) { + if(ledControl.ledCtrl(2, 0) < 0) + Toast.makeText(LedActivity.this, "打开绿灯失败", Toast.LENGTH_SHORT).show(); + else + imageGreen.setColorFilter(getResources().getColor(android.R.color.holo_green_light), PorterDuff.Mode.SRC_IN); + }else{ + if(ledControl.ledCtrl(2, 1) < 0) + Toast.makeText(LedActivity.this, "关闭绿灯失败", Toast.LENGTH_SHORT).show(); + else + imageGreen.setColorFilter(getResources().getColor(android.R.color.darker_gray), PorterDuff.Mode.SRC_IN); + } + } + }); + + ledControl.ledCtrl(0, 1); + ledControl.ledCtrl(1, 1); + ledControl.ledCtrl(2, 1); + } +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java new file mode 100644 index 0000000..a08b756 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java @@ -0,0 +1,12 @@ +package com.example.serial; +public class LedControl { + static{ + System.loadLibrary("serial"); + } + public static native int ledOpen(); + public static native int ledCtrl(int which, int status); + + static{ + System.loadLibrary("gpiod"); + } +} diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java new file mode 100644 index 0000000..051fea9 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java @@ -0,0 +1,70 @@ +package com.example.serial; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.example.serial.databinding.ActivityMainBinding; + +public class MainActivity extends AppCompatActivity { + private Button mBtnRS485; + private Button mBtnCan; + private Button mBtnLed; + private Button mBtnBuzzer; + private Button mBtnAdc; + + @Override + protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_menu); + + mBtnBuzzer = findViewById(R.id.btn_buzzer); + mBtnAdc = findViewById(R.id.btn_adc); + mBtnCan = findViewById(R.id.btn_can); + mBtnLed = findViewById(R.id.btn_led); + mBtnRS485 = findViewById(R.id.btn_rs485); + + setListener(); + } + + private void setListener(){ + OnClick onClick = new OnClick(); + + mBtnRS485.setOnClickListener(onClick); + mBtnLed.setOnClickListener(onClick); + mBtnCan.setOnClickListener(onClick); + mBtnBuzzer.setOnClickListener(onClick); + mBtnAdc.setOnClickListener(onClick); + } + + private class OnClick implements View.OnClickListener{ + @SuppressLint("NonConstantResourceId") + @Override + public void onClick(View v) { + Intent intent = null; + int id = v.getId(); + if (id == R.id.btn_rs485){ + intent = new Intent(MainActivity.this, RS485Activity.class); + }else if (id == R.id.btn_can){ + intent = new Intent(MainActivity.this, CanActivity.class); + }else if (id == R.id.btn_buzzer) { + intent = new Intent(MainActivity.this, BuzzerActivity.class); + }else if (id == R.id.btn_led) { + intent = new Intent(MainActivity.this, LedActivity.class); + }else if (id == R.id.btn_adc) { + intent = new Intent(MainActivity.this, AdcActivity.class); + } + startActivity(intent); + + } + } +} \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java new file mode 100644 index 0000000..0f23957 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java @@ -0,0 +1,69 @@ +package com.example.serial; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +public class MenuActivity extends AppCompatActivity { + private Button mBtnRS485; + private Button mBtnCan; + private Button mBtnLed; + private Button mBtnBuzzer; + private Button mBtnAdc; + + @Override + protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_menu); + + mBtnBuzzer = findViewById(R.id.btn_buzzer); + mBtnAdc = findViewById(R.id.btn_adc); + mBtnCan = findViewById(R.id.btn_can); + mBtnLed = findViewById(R.id.btn_led); + mBtnRS485 = findViewById(R.id.btn_rs485); + + setListener(); + } + + private void setListener(){ + OnClick onClick = new OnClick(); + + mBtnRS485.setOnClickListener(onClick); + mBtnLed.setOnClickListener(onClick); + mBtnCan.setOnClickListener(onClick); + mBtnBuzzer.setOnClickListener(onClick); + mBtnAdc.setOnClickListener(onClick); + } + + private class OnClick implements View.OnClickListener{ + @SuppressLint("NonConstantResourceId") + @Override + public void onClick(View v) { + Intent intent = null; + int id = v.getId(); + if (id == R.id.btn_rs485){ + intent = new Intent(MenuActivity.this, RS485Activity.class); + }else if (id == R.id.btn_can){ + intent = new Intent(MenuActivity.this, CanActivity.class); + }else if (id == R.id.btn_buzzer) { + intent = new Intent(MenuActivity.this, BuzzerActivity.class); + }else if (id == R.id.btn_led) { + intent = new Intent(MenuActivity.this, LedActivity.class); + }else if (id == R.id.btn_adc) { + intent = new Intent(MenuActivity.this, AdcActivity.class); + } + startActivity(intent); + + } + } +} diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java new file mode 100644 index 0000000..48f6354 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java @@ -0,0 +1,347 @@ + +package com.example.serial; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; + +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Bundle; +import android.text.method.ScrollingMovementMethod; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import com.example.serial.databinding.ActivityMainBinding; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + + +public class RS485Activity extends AppCompatActivity { + + // Used to load the 'serial' library on application startup. + static { + System.loadLibrary("serial"); + } + + // 与串口参数相关的组件 + private ActivityMainBinding binding; + private Spinner mSpBaud; + private Spinner mSpSerial; + private Spinner mSpData; + private Spinner mSpStop; + private Spinner mSpParity; + private Spinner mSpFlow; + + // 和发送信息相关的控件 + private Button mBtnClearSend; + private Button mBtnSend; + private EditText mEtSend; + // 和接收信息相关的控件 + private Button mBtnClearRev; + private TextView mTvRev; + private TextView mTvShow; + private Button mBtnRecv; + + // 打开串口&关闭串口 + private Button mBtnOpen; + private Button mBtnClose; + private Button mBtnSendState; + private Button mBtnRecvState; + + + // 设置串口的参数 + private String serial; + private long baudRate; + private int dataBits; + private double stopBits; + private int parity; + private int flowControl; // 0:None, 1:RTS/CTS, 2:XON/XOFF + + // 获取已经设定的值 + String[] serialArray; + String[] baudArray; + String[] dataArray; + String[] stopArray; + String[] parityArray; + String[] flowArray; + String sendData; + String currenState = null; + int sendOnTime; + int state = -1; + private static final int MSG_SEND_DATA = 1; + + boolean turnFlag = false; + private RS485Control RS485Control; + Runnable runnable; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_rs485); + + RS485Control = new RS485Control(); + // 找到控件 + mSpSerial = findViewById(R.id.sp_serial); + mSpBaud = findViewById(R.id.sp_baud); + mSpData = findViewById(R.id.sp_data); + mSpStop = findViewById(R.id.sp_stop); + mSpParity = findViewById(R.id.sp_parity); + mSpFlow = findViewById(R.id.sp_flow); + + mBtnOpen = findViewById(R.id.btn_open); + mBtnClose = findViewById(R.id.btn_close); + mBtnClearRev = findViewById(R.id.btn_clear_rev); + mBtnClearSend = findViewById(R.id.btn_clear_send); + mBtnSend = findViewById(R.id.btn_send); + mEtSend = findViewById(R.id.et_send); + mTvRev = findViewById(R.id.txt_rev); + mTvShow = findViewById(R.id.txt_show); + mBtnRecv = findViewById(R.id.btn_recv); + mBtnRecvState = findViewById(R.id.btn_recv_state); + mBtnSendState = findViewById(R.id.btn_send_state); + + // 还没有连接串口时,显示not connected + mTvShow.setTextColor(Color.parseColor("#FF0000")); + mTvShow.setText("NOT CONNECTED"); + mTvShow.setPaintFlags(mTvShow.getPaintFlags() | Paint.ANTI_ALIAS_FLAG); + // 设置并监听下拉列表 + setSpinner(mSpSerial); + setSpinner(mSpBaud); + setSpinner(mSpData); + setSpinner(mSpStop); + setSpinner(mSpParity); + setSpinner(mSpFlow); + + mTvRev.setMovementMethod(ScrollingMovementMethod.getInstance()); + + // 监听所有的button + setListener(); + + } + + @Override + protected void onDestroy() { + super.onDestroy(); + RS485Control.closeSerialPort(); + } + + private void setSpinner(Spinner spinner){ + ArrayAdapter<CharSequence> adapter; + int id = spinner.getId(); + + if (id == R.id.sp_serial){ + adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.serial_name, android.R.layout.simple_spinner_item); + }else if (id == R.id.sp_baud) { + adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.baud_rate, android.R.layout.simple_spinner_item); + } else if (id == R.id.sp_data) { + adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.data_bits, android.R.layout.simple_spinner_item); + } else if (id == R.id.sp_stop) { + adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.stop_bits, android.R.layout.simple_spinner_item); + } else if (id == R.id.sp_parity) { + adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.parity, android.R.layout.simple_spinner_item); + } else if (id == R.id.sp_flow) { + adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.flow_control, android.R.layout.simple_spinner_item); + } else { + throw new IllegalStateException("Unexpected value: " + spinner.getId()); + } + adapter.setDropDownViewResource(android.R.layout.simple_spinner_item); + spinner.setAdapter(adapter); + + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + int spinnerId = spinner.getId(); + + if (spinnerId == R.id.sp_serial) { + serialArray = getResources().getStringArray(R.array.serial_name); + serial = serialArray[position]; +// Toast.makeText(MainActivity.this, "串口:" + serial, Toast.LENGTH_SHORT).show(); + } else if (spinnerId == R.id.sp_baud) { + baudArray = getResources().getStringArray(R.array.baud_rate); + baudRate = Integer.parseInt(baudArray[position]); +// Toast.makeText(MainActivity.this, "波特率:" + baudRate, Toast.LENGTH_SHORT).show(); + } else if (spinnerId == R.id.sp_data) { + dataArray = getResources().getStringArray(R.array.data_bits); + dataBits = Integer.parseInt(dataArray[position]); +// Toast.makeText(MainActivity.this, "数据位:" + dataBits, Toast.LENGTH_SHORT).show(); + } else if (spinnerId == R.id.sp_stop) { + stopArray = getResources().getStringArray(R.array.stop_bits); + stopBits = Double.parseDouble(stopArray[position]); +// Toast.makeText(MainActivity.this, "停止位:" + stopBits, Toast.LENGTH_SHORT).show(); + } else if (spinnerId == R.id.sp_parity) { + parityArray = getResources().getStringArray(R.array.parity); + if (parityArray[position].equals("偶校验")) + parity = 2; + else if (parityArray[position].equals("奇校验")) + parity = 1; + else if (parityArray[position].equals("None")) + parity = 0; +// Toast.makeText(MainActivity.this, "校验位:" + parityArray[position], Toast.LENGTH_SHORT).show(); + } else if (spinnerId == R.id.sp_flow) { + flowArray = getResources().getStringArray(R.array.flow_control); + flowControl = position; + } else { + throw new IllegalStateException("Unexpected value: " + spinner.getId()); + } + } + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }); + } + + private void setListener(){ + OnClick onClick = new OnClick(); + mBtnOpen.setOnClickListener(onClick); + mBtnClose.setOnClickListener(onClick); + mBtnClearRev.setOnClickListener(onClick); + mBtnClearSend.setOnClickListener(onClick); + mBtnSend.setOnClickListener(onClick); + mBtnRecvState.setOnClickListener(onClick); + mBtnSendState.setOnClickListener(onClick); + mBtnRecv.setOnClickListener(onClick); + } + private class OnClick implements View.OnClickListener{ + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btn_open) { + if (turnFlag) { + Toast.makeText(RS485Activity.this, "串口已打开, 不能重复打开", Toast.LENGTH_SHORT).show(); + }else { + int rv = RS485Control.openSerialPort(serial, (int) baudRate, dataBits, parity, (int) stopBits, flowControl); + if(rv == 1){ + Toast.makeText(RS485Activity.this, "请添加"+serial+"的权限", Toast.LENGTH_SHORT).show(); + }else if (rv < 0) { + Toast.makeText(RS485Activity.this, "打开串口失败", Toast.LENGTH_SHORT).show(); + } else { + turnFlag = true; +// startReceiving(); // 串口打开后默认开始读 + serialOpened(); + Toast.makeText(RS485Activity.this, "成功打开串口", Toast.LENGTH_SHORT).show(); + } + } + } else if (id == R.id.btn_close){ + if (!turnFlag) { + Toast.makeText(RS485Activity.this, "串口已关闭,不能重复关闭", Toast.LENGTH_SHORT).show(); + }else { + // 关闭串口 + int rv = RS485Control.closeSerialPort(); +// Toast.makeText(MainActivity.this, "close", Toast.LENGTH_SHORT).show(); + if (rv < 0) { + Toast.makeText(RS485Activity.this, "关闭串口失败", Toast.LENGTH_SHORT).show(); + } else { + turnFlag = false; + serialClosed(); + Toast.makeText(RS485Activity.this, "关闭串口", Toast.LENGTH_SHORT).show(); + } + } + } else if (id == R.id.btn_clear_rev) { + // 清空接收区数据 + mTvRev.setText(""); + } else if (id == R.id.btn_clear_send) { + // 清空发送区内的数据 + mEtSend.getText().clear(); + } else if (id == R.id.btn_send) { + // 读取发送区中的数据 + sendData = mEtSend.getText().toString(); + + // 判断是否已连接串口, 如果没有连接串口则需先连接串口 + if( !turnFlag ){ + Toast.makeText(RS485Activity.this, "串口未打开,请打开串口!", Toast.LENGTH_SHORT).show(); + }else if(sendData.isEmpty()){ + // 判断发送区是否为空,如果为空则弹出提示 + Toast.makeText(RS485Activity.this, "发送区为空,请输入数据!", Toast.LENGTH_SHORT).show(); + } else if (state != 0) { + Toast.makeText(RS485Activity.this, "串口未处于发送状态,不能发送数据!", Toast.LENGTH_SHORT).show(); + } else { + int rv = RS485Control.sendToPort(sendData, sendData.length()); + if( rv < 0 ) + { + Toast.makeText(RS485Activity.this, "发送数据失败!", Toast.LENGTH_SHORT).show(); + } + Toast.makeText(RS485Activity.this, "发送数据成功", Toast.LENGTH_SHORT).show(); + + } + } else if (id == R.id.btn_send_state) { + // 当gpio4-io26为0时,处于发送模式 + if( RS485Control.changeState(0) < 0 ){ + Log.e("Change State for send", "Change State as send failed"); + }else { + state = 0; + if (turnFlag) + serialOpened(); + else + serialClosed(); + } + } else if (id == R.id.btn_recv_state) { + // 当gpio4-io26为1时,处于接收模式 + if (RS485Control.changeState(1) < 0) { + Log.e("Change State for recv", "Change State as recv failed"); + } else { + state = 1; + if (turnFlag) + serialOpened(); + else + serialClosed(); + } + } else if (id == R.id.btn_recv) { + // 当按下按钮后接收一次数据 + int len = 1024; + int timeout = 10; + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("GMT+8")); + String date = df.format(new Date()); + String buf = RS485Control.recvFromPort(len, timeout); // 超时时间设置为 1000 ms + if (buf != null) { + String revData = date + "\t" + buf; + String currentText = mTvRev.getText().toString(); + String newText = currentText + "\n" + revData; // 追加新数据 +// mTvRev.setText(newText); + mTvRev.append("\n" + revData); + mTvRev.invalidate(); + mTvRev.requestLayout(); + } else if (buf == null) { + System.err.println("Error receiving data."); + } + } + } + } + + private void serialClosed(){ + mTvShow.setTextColor(Color.parseColor("#FF0000")); + if(currenState == null){ + mTvShow.setText("NOT CONNECTED"); + } else if (currenState.equals("recv")) + mTvShow.setText("NOT CONNECTED"+"接收模式"); + else if (currenState.equals("send")) { + mTvShow.setText("NOT CONNECTED"+"发送模式"); + } + mTvShow.setPaintFlags(mTvShow.getPaintFlags() | Paint.ANTI_ALIAS_FLAG); + } + + private void serialOpened(){ + mTvShow.setTextColor(Color.parseColor("#1FA324")); + if (state == 1) + mTvShow.setText(serial+" OPENED, "+baudRate+", "+dataBits+", "+stopBits+", "+parity+", "+"接收模式"); + else if (state == 0) { + mTvShow.setText(serial+" OPENED, "+baudRate+", "+dataBits+", "+stopBits+", "+parity+", "+"发送模式"); + }else{ + mTvShow.setText(serial+" OPENED, "+baudRate+", "+dataBits+", "+stopBits+", "+parity); + } + + mTvShow.setPaintFlags(mTvShow.getPaintFlags() | Paint.ANTI_ALIAS_FLAG); + } +} diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java new file mode 100644 index 0000000..e99cb92 --- /dev/null +++ b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java @@ -0,0 +1,18 @@ +package com.example.serial; + +public class RS485Control { + static { + System.loadLibrary("serial"); + } + + public static native int openSerialPort(String path, long baudRate, int dataBits, int parity, int stopBits, int flowControl); + public static native int closeSerialPort(); + + public static native int sendToPort(String msg, int len); + public static native String recvFromPort(int len, int timeout); + + public static native int changeState(int state); // + + public static native String saveToFile(String msg, int len, String fileName); + +} diff --git a/app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so b/app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so new file mode 100644 index 0000000..e1a7642 --- /dev/null +++ b/app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/drawable/background.xml b/app_sdk/app_src/app/src/main/res/drawable/background.xml new file mode 100644 index 0000000..c790b42 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/background.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:color="@color/white" + android:width="2dp"/> + <corners + android:radius="5dp"/> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg1.jpg b/app_sdk/app_src/app/src/main/res/drawable/bg1.jpg new file mode 100644 index 0000000..b7ede45 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/bg1.jpg Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg2.jpg b/app_sdk/app_src/app/src/main/res/drawable/bg2.jpg new file mode 100644 index 0000000..85df69e --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/bg2.jpg Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml b/app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml new file mode 100644 index 0000000..ad1402a --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> +<!-- <solid--> +<!-- android:color="#FFFFFF"/>--> + <stroke + android:width="2dp" + android:color="#BDB6B9"/> + <corners + android:radius="5dp"/> + <padding + android:bottom="0.1dp" + android:left="0.5dp" + android:right="0.5dp" + android:top="0dp"/> + <solid + android:color="#E7D9EA"/> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg_username.xml b/app_sdk/app_src/app/src/main/res/drawable/bg_username.xml new file mode 100644 index 0000000..6395d2c --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/bg_username.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:width="1.5dp" + android:color="@color/white" + /> + + + <corners + android:radius="5dp" + /> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml new file mode 100644 index 0000000..e1333cd --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:width="1dp" + android:color="@color/white" + /> + + <corners + android:radius="10dp" + /> + + <solid + android:color="@color/white"/> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/bt_left.xml b/app_sdk/app_src/app/src/main/res/drawable/bt_left.xml new file mode 100644 index 0000000..e792ddf --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/bt_left.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:width="1.5dp" + android:color="@color/white" + /> + + <corners + android:topRightRadius="5dp" + android:bottomRightRadius="5dp" + /> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/bt_right.xml b/app_sdk/app_src/app/src/main/res/drawable/bt_right.xml new file mode 100644 index 0000000..77f8f76 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/bt_right.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:width="1.5dp" + android:color="@color/white" + /> + + <corners + android:topLeftRadius="5dp" + android:bottomLeftRadius="5dp" + /> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/button_selector.xml b/app_sdk/app_src/app/src/main/res/drawable/button_selector.xml new file mode 100644 index 0000000..9b7fd4c --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/button_selector.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" + android:drawable="@drawable/shape_pressed"/> + <item android:state_checked="false" + android:drawable="@drawable/shape_normal"/> +</selector> diff --git a/app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml new file mode 100644 index 0000000..0888d6a --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:width="2dp" + android:color="@color/white"/> + <solid + android:color="@color/white"/> + <corners + android:radius="5dp"/> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml b/app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml new file mode 100644 index 0000000..0ebc7e9 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:width="1dp" + android:color="@color/black" + /> + <solid + android:color="@color/white"/> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml new file mode 100644 index 0000000..b8829df --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <gradient + android:angle="90" + android:endColor="#DCD6D6" + android:centerColor="#CEB7EA" + android:startColor="@color/purple_700" /> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:fillColor="#3DDC84" + android:pathData="M0,0h108v108h-108z" /> + <path + android:fillColor="#00000000" + android:pathData="M9,0L9,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,0L19,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M29,0L29,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M39,0L39,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M49,0L49,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M59,0L59,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M69,0L69,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M79,0L79,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M89,0L89,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M99,0L99,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,9L108,9" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,19L108,19" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,29L108,29" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,39L108,39" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,49L108,49" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,59L108,59" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,69L108,69" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,79L108,79" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,89L108,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,99L108,99" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,29L89,29" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,39L89,39" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,49L89,49" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,59L89,59" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,69L89,69" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,79L89,79" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M29,19L29,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M39,19L39,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M49,19L49,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M59,19L59,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M69,19L69,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M79,19L79,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> +</vector> diff --git a/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="85.84757" + android:endY="92.4963" + android:startX="42.9492" + android:startY="49.59793" + android:type="linear"> + <item + android:color="#44000000" + android:offset="0.0" /> + <item + android:color="#00000000" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" + android:strokeWidth="1" + android:strokeColor="#00000000" /> +</vector> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml new file mode 100644 index 0000000..b470b19 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke + android:width="2dp" + android:color="@color/white"/> + <corners + android:radius="10dp"/> + <solid + android:color="@color/white"/> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml b/app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml new file mode 100644 index 0000000..1ec89ac --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/colorButtonNormal" /> + <stroke android:width="1dip" android:color="@color/white"/> + <!-- 圆角 --> + <corners android:radius="10dp" /> + <!-- 边距 --> + <padding + android:bottom="5dp" + android:left="5dp" + android:right="5dp" + android:top="5dp" /> +</shape> diff --git a/app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml b/app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml new file mode 100644 index 0000000..c2eb775 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/colorButtonPressed" /> + <stroke android:width="1dip" android:color="@color/colorButtonPressed"/> + <!-- 圆角 --> + <corners android:radius="10dp" /> + <!-- 边距 --> + <padding + android:bottom="5dp" + android:left="5dp" + android:right="5dp" + android:top="5dp" /> +</shape> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_adc.xml b/app_sdk/app_src/app/src/main/res/layout/activity_adc.xml new file mode 100644 index 0000000..0bc116f --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/layout/activity_adc.xml @@ -0,0 +1,322 @@ +<?xml version="1.0" encoding="utf-8"?> +<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:background="@drawable/gradient_bg" + android:orientation="vertical" + tools:context=".MainActivity" + android:hardwareAccelerated="true"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="ADC通信" + android:textSize="25sp" + android:textColor="#673AB7" + android:layout_marginTop="20dp" + android:layout_marginRight="10dp" + android:layout_marginLeft="10dp" + android:layout_gravity="center"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="串口设置" + android:textSize="17sp" + android:textColor="#E91E63" + android:layout_marginRight="10dp" + android:layout_marginLeft="10dp"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:background="@drawable/bg_frame" + android:orientation="vertical"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:baselineAligned="false" + android:gravity="center" + android:orientation="horizontal"> + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_marginRight="40dp" + android:layout_weight="1" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="串口号" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_serial" + android:layout_width="150dp" + android:layout_height="40dp" + /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_gravity="right" + android:layout_weight="1" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="波特率" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_baud" + android:layout_width="150dp" + android:layout_height="40dp" + android:gravity="center" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_marginRight="40dp" + android:layout_weight="1" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="数据位" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_data" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:gravity="center" + android:orientation="horizontal"> + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="停止位" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_stop" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:layout_gravity="right" + android:layout_marginLeft="30dp" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="校验位" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_parity" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:orientation="horizontal" + android:layout_marginRight="30dp"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="流控制" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_flow" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:text="串口开/关" + android:textColor="#172DA6" + android:textSize="17sp" + android:layout_marginLeft="10dp"/> + <TextView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:text="操作设置" + android:textColor="#009688" + android:textSize="17sp" + android:layout_marginLeft="40dp"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="5dp"> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:background="@drawable/bg_frame" + android:gravity="center" + android:orientation="horizontal"> + <Button + android:id="@+id/adc_btn_open" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="打开串口" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" + android:layout_marginRight="40dp"/> + <Button + android:id="@+id/adc_btn_close" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="关闭串口" + android:textColor="@color/black" + android:theme="@style/MyRadioButton"/> + + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:background="@drawable/bg_frame" + android:layout_marginRight="10dp"> + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:orientation="horizontal" + android:layout_marginRight="0dp" + android:gravity="center"> + <TextView + android:layout_width="100dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="电流(1)/电压(2)" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_operate" + android:layout_width="100dp" + android:layout_height="40dp" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:orientation="horizontal" + android:layout_marginRight="0dp" + android:gravity="center"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="通道" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/adc_sp_channel" + android:layout_width="100dp" + android:layout_height="40dp" /> + </LinearLayout> + </LinearLayout> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="right"> + <Button + android:id="@+id/adc_btn_send" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="发送" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" + android:layout_gravity="right" + android:layout_marginRight="20dp"/> + <Button + android:id="@+id/adc_btn_recv" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="清空缓冲区" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" + android:layout_gravity="right" + android:layout_marginRight="20dp"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ScrollView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <TextView + android:id="@+id/adc_txt_rev" + android:layout_width="match_parent" + android:layout_height="100dp" + android:layout_marginLeft="10dp" + android:layout_marginTop="10dp" + android:layout_marginRight="10dp" + android:background="@color/white" + android:hint="接收数据显示" + android:textColor="@color/black" + android:textSize="15sp" /> + </LinearLayout> + </ScrollView> + </LinearLayout> + + + +</LinearLayout> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml b/app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml new file mode 100644 index 0000000..af3db71 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<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" + tools:context=".MainActivity" + android:orientation="vertical"> + + <TextView + android:id="@+id/sample_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="蜂鸣器" + android:textSize="40sp" + android:layout_marginTop="50dp" + android:layout_gravity="center"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginLeft="100dp" + android:layout_marginRight="100dp"> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="50dp" + android:layout_marginLeft="150dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="周期(ns):" + android:textSize="20sp" + android:textColor="@color/black" + /> + <EditText + android:id="@+id/period" + android:layout_width="200dp" + android:layout_height="wrap_content" + android:textSize="15sp" + android:hint="1000000" + + android:textColor="@color/black" + android:layout_marginLeft="10dp"/> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="50dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="占空比(ns):" + android:textSize="20sp" + android:textColor="@color/black" + /> + <EditText + android:id="@+id/duty_cycle" + android:layout_width="200dp" + android:layout_height="wrap_content" + android:textSize="15sp" + android:hint="50000" + android:textColor="@color/black" + android:layout_marginLeft="10dp"/> + </LinearLayout> + </LinearLayout> + + </LinearLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginTop="20dp" + android:orientation="horizontal"> + <Button + android:id="@+id/play" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="播放" + android:textSize="15sp" + /> + + <Button + android:id="@+id/stop" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="暂停" + android:textSize="15sp" + android:layout_marginLeft="100dp"/> + </LinearLayout> + + + + + + +</LinearLayout> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_can.xml b/app_sdk/app_src/app/src/main/res/layout/activity_can.xml new file mode 100644 index 0000000..1d49412 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/layout/activity_can.xml @@ -0,0 +1,271 @@ +<?xml version="1.0" encoding="utf-8"?> +<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" + android:layout_gravity="center" + tools:context=".MainActivity" + android:background="@drawable/bg1"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="CAN通信" + android:layout_gravity="center" + android:textColor="@color/white" + android:textSize="20dp" + android:textStyle="bold" + android:layout_marginTop="30dp"/> + <TextView + android:layout_marginTop="10dp" + android:id="@+id/sample_text" + android:layout_marginLeft="30dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="发送数据设置" + android:textSize="20sp" + android:textColor="@color/white"/> + + <LinearLayout + android:gravity="center" + android:layout_marginLeft="30dp" + android:layout_marginRight="30dp" + android:layout_marginTop="10dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:background="@drawable/background"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="10dp" + android:gravity="left"> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:layout_marginLeft="20dp" + android:orientation="horizontal" + android:gravity="left"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="CAN:" + android:textColor="@color/black" + android:gravity="center"/> + <Spinner + android:id="@+id/sp_can" + android:layout_width="120dp" + android:layout_height="match_parent" + android:layout_marginLeft="5dp" + /> + </LinearLayout> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="ID(HEX):" + android:textSize="15sp" + android:gravity="center" + android:textColor="@color/black"/> + <EditText + android:id="@+id/can_ed_id" + android:layout_width="60dp" + android:layout_height="wrap_content" + android:textAlignment="center" + android:layout_marginLeft="5dp" + android:text="000" + android:textSize="15sp" + android:textColor="@color/black" + android:background="@drawable/ed_bacground" + /> + </LinearLayout> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginBottom="10dp" + android:layout_marginTop="5dp" + android:layout_marginLeft="13dp" + android:gravity="left"> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="DLC(0...7):" + android:layout_gravity="left" + android:textSize="15sp" + android:textColor="@color/black"/> + <EditText + android:id="@+id/can_ed_dlc" + android:layout_width="90dp" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="2" + android:textSize="15sp" + android:layout_marginLeft="5dp" + android:textColor="@color/black" + android:background="@drawable/ed_bacground" + /> + </LinearLayout> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="3" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + android:layout_marginLeft="190dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Data(HEX):" + android:textSize="15sp" + android:textColor="@color/black"/> + <EditText + android:id="@+id/can_ed_data" + android:layout_width="300dp" + android:layout_height="wrap_content" + android:layout_marginLeft="5dp" + android:text=" 00 00 " + android:textSize="15sp" + android:textColor="@color/black" + android:background="@drawable/ed_bacground" + /> + </LinearLayout> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <LinearLayout + android:layout_marginTop="10dp" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center"> + <Button + android:id="@+id/can_send" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:layout_marginLeft="50dp" + android:background="@drawable/bt_bg" + android:text="发送" + android:textColor="@color/black" + android:theme="@style/MyRadioButton"/> + <Button + android:id="@+id/can_send_clear" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:layout_marginRight="50dp" + android:layout_marginLeft="50dp" + android:layout_gravity="right" + android:background="@drawable/bt_bg" + android:text="清空发送区" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" /> + </LinearLayout> + + <LinearLayout + android:layout_marginTop="10dp" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center"> + <Button + android:id="@+id/can_recv" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:background="@drawable/bt_bg" + android:text="接收" + android:textColor="@color/black" + android:theme="@style/MyRadioButton"/> + <Button + android:id="@+id/can_recv_clear" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:layout_marginRight="50dp" + android:layout_marginLeft="50dp" + android:layout_gravity="right" + android:background="@drawable/bt_bg" + android:text="清空接收区" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" /> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="10dp"> + <ScrollView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="150dp"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp" + android:layout_marginRight="30dp" + android:layout_marginLeft="30dp"> + <TextView + android:layout_width="25dp" + android:layout_height="150dp" + android:text="发送区" + android:textSize="15sp" + android:background="@color/white"/> + <TextView + android:id="@+id/can_tv_send" + android:layout_width="340dp" + android:layout_height="150dp" + android:background="@color/white" + /> + </LinearLayout> + </ScrollView> + + <ScrollView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="150dp"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp" + android:layout_marginRight="30dp"> + <TextView + android:layout_width="25dp" + android:layout_height="150dp" + android:text="接收区" + android:textSize="15sp" + android:background="@color/white"/> + <TextView + android:id="@+id/can_tv_recv" + android:layout_width="340dp" + android:layout_height="150dp" + android:background="@color/white" + /> + </LinearLayout> + </ScrollView> + </LinearLayout> +</LinearLayout> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_led.xml b/app_sdk/app_src/app/src/main/res/layout/activity_led.xml new file mode 100644 index 0000000..946e27f --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/layout/activity_led.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.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=".LedActivity"> + + <Button + android:id="@+id/yellow" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="yellow" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.506" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.519" /> + + <Button + android:id="@+id/red" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="red" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.146" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.519" /> + + <TextView + android:id="@+id/sample_text" + android:layout_width="200dp" + android:layout_height="45dp" + android:text="LedDemo RK3568" + android:textSize="20sp" + android:gravity="center" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.286" /> + + <Button + android:id="@+id/green" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="green" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.873" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.519" /> + + <ImageView + android:id="@+id/imageRed" + android:layout_width="45dp" + android:layout_height="45dp" + app:srcCompat="@android:drawable/presence_invisible" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.15" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.4" + tools:layout_editor_absoluteX="59dp" + tools:layout_editor_absoluteY="289dp" /> + + <ImageView + android:id="@+id/imageGreen" + android:layout_width="45dp" + android:layout_height="45dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.86" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.4" + app:srcCompat="@android:drawable/presence_invisible" /> + + <ImageView + android:id="@+id/imageYellow" + android:layout_width="45dp" + android:layout_height="45dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.4" + app:srcCompat="@android:drawable/presence_invisible" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_main.xml b/app_sdk/app_src/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..048fd6a --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="utf-8"?> +<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" + android:background="@drawable/bg2" + tools:context=".MainActivity"> + + <TextView + android:id="@+id/sample_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="RK3568" + android:textColor="@color/white" + android:textSize="25sp" + android:gravity="center" + android:maxLines="1" + android:maxEms="20" + android:ellipsize="end" + android:layout_marginTop="100dp" + /> + <EditText + android:id="@+id/ed_1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="50dp" + android:gravity="left" + android:textSize="15sp" + android:textColor="@color/white" + android:hint="用户名" + android:textColorHint="@color/white" + android:maxLines="1" + android:padding="10dp" + android:background="@drawable/bg_username" + android:layout_margin="10dp" + /> + <EditText + android:id="@+id/ed_2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:gravity="left" + android:textSize="15sp" + android:textColor="@color/white" + android:textColorHint="@color/white" + android:hint="密码" + android:maxLines="1" + android:padding="10dp" + android:inputType="textPassword" + android:background="@drawable/bg_username" + android:layout_margin="10dp" + /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="20dp"> + + <Button + android:id="@+id/btn_1" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:text="登录" + android:textColor="@color/black" + android:layout_marginLeft="10dp" + android:background="@drawable/bt_right" + android:theme="@style/MyRadioButton" + /> + <Button + android:id="@+id/btn_2" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:text="注册" + android:textColor="@color/black" + android:layout_marginLeft="5dp" + android:layout_marginRight="10dp" + android:background="@drawable/bt_left" + android:theme="@style/MyRadioButton" + /> + </LinearLayout> + +</LinearLayout> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_menu.xml b/app_sdk/app_src/app/src/main/res/layout/activity_menu.xml new file mode 100644 index 0000000..820beff --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/layout/activity_menu.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:background="@color/white" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="60dp" + android:orientation="horizontal" + android:gravity="center" + android:background="#8F5A5A" + android:layout_marginTop="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="RK3568测试目录" + android:textColor="@color/white" + android:textSize="20sp" + android:textStyle="bold" + android:layout_marginLeft="20dp"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="15dp" + android:layout_marginTop="15dp" + android:orientation="vertical"> + <Button + android:id="@+id/btn_rs485" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="RS485通信"/> + + <Button + android:id="@+id/btn_led" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:text="三色LED灯"/> + + <Button + android:id="@+id/btn_can" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:text="CAN通信"/> + + <Button + android:id="@+id/btn_buzzer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:text="蜂鸣器"/> + + <Button + android:id="@+id/btn_adc" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:text="ADC采样"/> + + + </LinearLayout> + +</LinearLayout> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml b/app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml new file mode 100644 index 0000000..ba0b373 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml @@ -0,0 +1,423 @@ +<?xml version="1.0" encoding="utf-8"?> +<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:background="@drawable/gradient_bg" + android:orientation="vertical" + tools:context=".RS485Activity" + android:hardwareAccelerated="true"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="RS485通信" + android:textSize="25sp" + android:textColor="#673AB7" + android:layout_marginTop="20dp" + android:layout_marginRight="10dp" + android:layout_marginLeft="10dp" + android:layout_gravity="center"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="串口设置" + android:textSize="17sp" + android:textColor="#E91E63" + android:layout_marginTop="0dp" + android:layout_marginRight="10dp" + android:layout_marginLeft="10dp"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:background="@drawable/bg_frame" + android:orientation="vertical"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:baselineAligned="false" + android:gravity="center" + android:orientation="horizontal"> + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_marginRight="40dp" + android:layout_weight="1" + android:orientation="horizontal"> + + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="串口号" + android:textColor="@color/black"/> + + <Spinner + android:id="@+id/sp_serial" + android:layout_width="150dp" + android:layout_height="40dp" + /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_gravity="right" + android:layout_weight="1" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="波特率" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/sp_baud" + android:layout_width="150dp" + android:layout_height="40dp" + android:gravity="center" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_marginRight="40dp" + android:layout_weight="1" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="数据位" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/sp_data" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:gravity="center" + android:orientation="horizontal"> + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="停止位" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/sp_stop" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:layout_gravity="right" + android:layout_marginLeft="30dp" + android:orientation="horizontal"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="校验位" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/sp_parity" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="40dp" + android:layout_weight="1" + android:orientation="horizontal" + android:layout_marginRight="30dp"> + <TextView + android:layout_width="60dp" + android:layout_height="match_parent" + android:gravity="center" + android:text="流控制" + android:textColor="@color/black"/> + <Spinner + android:id="@+id/sp_flow" + android:layout_width="150dp" + android:layout_height="40dp" /> + </LinearLayout> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:text="串口开/关" + android:textColor="#172DA6" + android:textSize="17sp" + android:layout_marginLeft="10dp"/> + <TextView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:text="串口状态" + android:textColor="#009688" + android:textSize="17sp" + android:layout_marginLeft="40dp"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="5dp"> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:background="@drawable/bg_frame" + android:gravity="center" + android:orientation="horizontal"> + <Button + android:id="@+id/btn_open" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="打开串口" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" + android:layout_marginRight="40dp"/> + <Button + android:id="@+id/btn_close" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="关闭串口" + android:textColor="@color/black" + android:theme="@style/MyRadioButton"/> + + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:background="@drawable/bg_frame" + android:gravity="center" + android:orientation="horizontal"> + <Button + android:id="@+id/btn_send_state" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="发送模式" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" + android:layout_marginRight="40dp"/> + <Button + android:id="@+id/btn_recv_state" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:text="接收模式" + android:textColor="@color/black" + android:theme="@style/MyRadioButton"/> + </LinearLayout> + </LinearLayout> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginTop="5dp"> + + </LinearLayout> + + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="left" + android:layout_marginTop="5dp" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp"> + + + + <TextView + android:id="@+id/txt_show" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:textColor="@color/black" + android:textSize="13sp" + android:layout_marginLeft="10dp" + android:gravity="left|bottom" + android:layout_marginBottom="5dp" + /> + + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> +<!-- <EditText--> +<!-- android:id="@+id/et_send"--> +<!-- android:layout_width="0dp"--> +<!-- android:layout_weight="1"--> +<!-- android:layout_height="100dp"--> +<!-- android:layout_marginLeft="10dp"--> +<!-- android:layout_marginTop="10dp"--> +<!-- android:layout_marginRight="10dp"--> +<!-- android:background="@color/white"--> +<!-- android:hint="发送信息"--> +<!-- android:textSize="15sp"--> +<!-- android:textColor="@color/black" />--> + <ScrollView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content"> + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + <EditText + android:id="@+id/et_send" + android:layout_width="match_parent" + android:layout_height="100dp" + android:layout_marginLeft="10dp" + android:layout_marginTop="10dp" + android:layout_marginRight="10dp" + android:background="@color/white" + android:hint="发送信息" + android:textSize="15sp" + android:textColor="@color/black" /> + </TableRow> + </ScrollView> + + <ScrollView + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:id="@+id/txt_rev" + android:layout_width="match_parent" + android:layout_height="100dp" + android:layout_marginLeft="10dp" + android:layout_marginTop="10dp" + android:layout_marginRight="10dp" + android:background="@color/white" + android:textColor="@color/black" + /> + </LinearLayout> + </ScrollView> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="10dp"> + <LinearLayout + android:layout_width="0dp" + android:layout_weight="3" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginLeft="10dp"> + + <Button + android:id="@+id/btn_send" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:layout_marginTop="5dp" + android:layout_marginRight="10dp" + android:layout_marginLeft="20dp" + android:background="@drawable/bt_bg" + android:text="发送" + android:textColor="@color/black" + android:theme="@style/MyRadioButton"/> + + <Button + android:id="@+id/btn_clear_send" + android:layout_width="wrap_content" + android:layout_height="45dp" + android:layout_marginTop="5dp" + android:layout_marginRight="10dp" + android:background="@drawable/bt_bg" + android:text="清空发送区" + android:textColor="@color/black" + android:theme="@style/MyRadioButton"/> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <Button + android:id="@+id/btn_recv" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="45dp" + android:layout_gravity="right" + android:layout_marginTop="5dp" + android:layout_marginRight="10dp" + android:background="@drawable/bt_bg" + android:text="接收" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" + /> + + <Button + android:id="@+id/btn_clear_rev" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="45dp" + android:layout_gravity="right" + android:layout_marginTop="5dp" + android:layout_marginRight="10dp" + android:background="@drawable/bt_bg" + android:text="清空缓冲区" + android:textColor="@color/black" + android:theme="@style/MyRadioButton" + /> + </LinearLayout> + </LinearLayout> + + + +</LinearLayout> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp Binary files differ diff --git a/app_sdk/app_src/app/src/main/res/values-night/themes.xml b/app_sdk/app_src/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..7e90457 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Base application theme. --> + <style name="Theme.Serial" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> + <!-- Primary brand color. --> + <item name="colorPrimary">@color/purple_200</item> + <item name="colorPrimaryVariant">@color/purple_700</item> + <item name="colorOnPrimary">@color/black</item> + <!-- Secondary brand color. --> + <item name="colorSecondary">@color/teal_200</item> + <item name="colorSecondaryVariant">@color/teal_200</item> + <item name="colorOnSecondary">@color/black</item> + <!-- Status bar color. --> + <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> + <!-- Customize your theme here. --> + </style> +</resources> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/values/colors.xml b/app_sdk/app_src/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..06e2f5f --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="purple_200">#FFBB86FC</color> + <color name="purple_500">#FF6200EE</color> + <color name="purple_700">#FF3700B3</color> + <color name="teal_200">#FF03DAC5</color> + <color name="teal_700">#FF018786</color> + <color name="black">#FF000000</color> + <color name="white">#FFFFFFFF</color> + <color name="hui">#1FA324</color> + <color name="red">#DC0707</color> + <color name="colorButtonPressed">#5D6FD6</color> <!-- 灰色 --> + <color name="colorButtonNormal">#FFF7F7</color> <!-- 默认颜色 --> +</resources> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/values/strings.xml b/app_sdk/app_src/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..ac24100 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/values/strings.xml @@ -0,0 +1,78 @@ +<resources> + <string name="app_name">RK3568</string> + <string-array name="serial_name"> + <item>/dev/ttyS9</item> + </string-array> + <string-array name="adc_serial_name"> + <item>/dev/ttyS6</item> + </string-array> + <string-array name="baud_rate"> + <item>110</item> + <item>300</item> + <item>600</item> + <item>1200</item> + <item>2400</item> + <item>2400</item> + <item>4800</item> + <item>9600</item> + <item>14400</item> + <item>19200</item> + <item>38400</item> + <item>56000</item> + <item>57600</item> + <item>115200</item> + <item>128000</item> + <item>120400</item> + <item>256000</item> + <item>460800</item> + <item>500000</item> + <item>512000</item> + <item>600000</item> + <item>750000</item> + <item>921600</item> + <item>150000</item> + <item>200000</item> + </string-array> + <string-array name="data_bits"> + <item>5</item> + <item>6</item> + <item>7</item> + <item>8</item> + </string-array> + <string-array name="stop_bits"> + <item>1</item> + <item>1.5</item> + <item>2</item> + </string-array> + <string-array name="parity"> + <item>None</item> + <item>奇校验</item> + <item>偶校验</item> + </string-array> + <string-array name="flow_control"> + <item>None</item> + <item>RTS/CTS</item> + <item>XON/XOFF</item> + </string-array> + <string-array name="can_name"> + <item>can0</item> + <item>can1</item> + </string-array> + <string-array name="bitrate"> + <item>1000000</item> + <item>800000</item> + <item>500000</item> + <item>2500000</item> + <item>100000</item> + </string-array> + <string-array name="adc_operate"> + <item>1</item> + <item>2</item> + </string-array> + <string-array name="adc_channel"> + <item>0</item> + <item>1</item> + <item>2</item> + <item>3</item> + </string-array> +</resources> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/values/themes.xml b/app_sdk/app_src/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..ab43507 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/values/themes.xml @@ -0,0 +1,23 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Base application theme. --> + <style name="Theme.Serial" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> + <!-- Primary brand color. --> + <item name="colorPrimary">@color/purple_500</item> + <item name="colorPrimaryVariant">@color/purple_700</item> + <item name="colorOnPrimary">@color/white</item> + <!-- Secondary brand color. --> + <item name="colorSecondary">@color/teal_200</item> + <item name="colorSecondaryVariant">@color/teal_700</item> + <item name="colorOnSecondary">@color/black</item> + <!-- Status bar color. --> + <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> + <!-- Customize your theme here. --> + </style> + <style name="MyRadioButton" parent="Theme.AppCompat.Light"> + <item name="colorControlNormal">@color/black</item> + <item name="colorControlActivated">@color/purple_200</item> + </style> + <style name="MenuButton" parent="Theme.AppCompat.DayNight.Dialog"> + <item name="colorControlNormal">@color/purple_200</item> + </style> +</resources> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/xml/backup_rules.xml b/app_sdk/app_src/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Sample backup rules file; uncomment and customize as necessary. + See https://developer.android.com/guide/topics/data/autobackup + for details. + Note: This file is ignored for devices older that API 31 + See https://developer.android.com/about/versions/12/backup-restore +--> +<full-backup-content> + <!-- + <include domain="sharedpref" path="."/> + <exclude domain="sharedpref" path="device.xml"/> +--> +</full-backup-content> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml b/app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Sample data extraction rules file; uncomment and customize as necessary. + See https://developer.android.com/about/versions/12/backup-restore#xml-changes + for details. +--> +<data-extraction-rules> + <cloud-backup> + <!-- TODO: Use <include> and <exclude> to control what is backed up. + <include .../> + <exclude .../> + --> + </cloud-backup> + <!-- + <device-transfer> + <include .../> + <exclude .../> + </device-transfer> + --> +</data-extraction-rules> \ No newline at end of file diff --git a/app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java b/app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java new file mode 100644 index 0000000..3becf4f --- /dev/null +++ b/app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.example.serial; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/app_sdk/app_src/build.gradle.kts b/app_sdk/app_src/build.gradle.kts new file mode 100644 index 0000000..3756278 --- /dev/null +++ b/app_sdk/app_src/build.gradle.kts @@ -0,0 +1,4 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + alias(libs.plugins.android.application) apply false +} \ No newline at end of file diff --git a/app_sdk/app_src/gradle.properties b/app_sdk/app_src/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/app_sdk/app_src/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/app_sdk/app_src/gradle/libs.versions.toml b/app_sdk/app_src/gradle/libs.versions.toml new file mode 100644 index 0000000..ffe5f54 --- /dev/null +++ b/app_sdk/app_src/gradle/libs.versions.toml @@ -0,0 +1,22 @@ +[versions] +agp = "8.4.0" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" +constraintlayout = "2.1.4" +activity = "1.8.0" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } + diff --git a/app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c --- /dev/null +++ b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar Binary files differ diff --git a/app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..b51df17 --- /dev/null +++ b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon May 13 22:16:04 CST 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/app_sdk/app_src/gradlew b/app_sdk/app_src/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/app_sdk/app_src/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/app_sdk/app_src/gradlew.bat b/app_sdk/app_src/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/app_sdk/app_src/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/app_sdk/app_src/settings.gradle.kts b/app_sdk/app_src/settings.gradle.kts new file mode 100644 index 0000000..acccceb --- /dev/null +++ b/app_sdk/app_src/settings.gradle.kts @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "Serial" +include(":app") diff --git a/app_sdk/bin/rk3568test.apk b/app_sdk/bin/rk3568test.apk new file mode 100644 index 0000000..415bd4a --- /dev/null +++ b/app_sdk/bin/rk3568test.apk Binary files differ -- Gitblit v1.9.1