app_sdk/app_src/.gitignore
New file @@ -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 app_sdk/app_src/.idea/.gitignore
New file @@ -0,0 +1,3 @@ # Default ignored files /shelf/ /workspace.xml app_sdk/app_src/.idea/compiler.xml
New file @@ -0,0 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CompilerConfiguration"> <bytecodeTargetLevel target="17" /> </component> </project> app_sdk/app_src/.idea/deploymentTargetSelector.xml
New file @@ -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> app_sdk/app_src/.idea/gradle.xml
New file @@ -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> app_sdk/app_src/.idea/migrations.xml
New file @@ -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> app_sdk/app_src/.idea/misc.xml
New file @@ -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> app_sdk/app_src/.idea/vcs.xml
New file @@ -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> app_sdk/app_src/app/.gitignore
New file @@ -0,0 +1 @@ /build app_sdk/app_src/app/build.gradle.kts
New file @@ -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) } app_sdk/app_src/app/proguard-rules.pro
New file @@ -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 app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java
New file @@ -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()); } } app_sdk/app_src/app/src/main/AndroidManifest.xml
New file @@ -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> app_sdk/app_src/app/src/main/cpp/CMakeLists.txt
New file @@ -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) app_sdk/app_src/app/src/main/cpp/adc_control.cpp
New file @@ -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); } app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp
New file @@ -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; } app_sdk/app_src/app/src/main/cpp/can_control.cpp
New file @@ -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; } app_sdk/app_src/app/src/main/cpp/include/gpiod.h
New file @@ -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__ */ app_sdk/app_src/app/src/main/cpp/led_control.cpp
New file @@ -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; } app_sdk/app_src/app/src/main/cpp/native-lib.cpp
New file @@ -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; } app_sdk/app_src/app/src/main/cpp/rs485_control.cpp
New file @@ -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; } app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java
New file @@ -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(" "); } } } } app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java
New file @@ -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); } app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java
New file @@ -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(); } } }); } } app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java
New file @@ -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表示输入的值无效 } app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java
New file @@ -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(); } } app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java
New file @@ -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); } app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java
New file @@ -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); } } app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java
New file @@ -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"); } } app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java
New file @@ -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); } } } app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java
New file @@ -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); } } } app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java
New file @@ -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); } } app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java
New file @@ -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); } app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.soBinary files differ
app_sdk/app_src/app/src/main/res/drawable/background.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/bg1.jpg
app_sdk/app_src/app/src/main/res/drawable/bg2.jpg
app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/bg_username.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/bt_left.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/bt_right.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/button_selector.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/layout/activity_adc.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/layout/activity_can.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/layout/activity_led.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/layout/activity_main.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/layout/activity_menu.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webpBinary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webpBinary files differ
app_sdk/app_src/app/src/main/res/values-night/themes.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/values/colors.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/values/strings.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/values/themes.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/xml/backup_rules.xml
New file @@ -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> app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml
New file @@ -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> app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java
New file @@ -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); } } app_sdk/app_src/build.gradle.kts
New file @@ -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 } app_sdk/app_src/gradle.properties
New file @@ -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 app_sdk/app_src/gradle/libs.versions.toml
New file @@ -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" } app_sdk/app_src/gradle/wrapper/gradle-wrapper.jarBinary files differ
app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties
New file @@ -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 app_sdk/app_src/gradlew
New file @@ -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" "$@" app_sdk/app_src/gradlew.bat
New file @@ -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 app_sdk/app_src/settings.gradle.kts
New file @@ -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") app_sdk/bin/rk3568test.apkBinary files differ