# 一、安装及测试 ## 1. 安装apk软件 通过**adb**安装apk软件: **方法一**: [Android adb启动任意app的几种方式_adb启动某个app的方法-CSDN博客](https://blog.csdn.net/ezconn/article/details/99885715) [彻底解决INSTALL_FAILED_UPDATE_INCOMPATIBLE的安装错误-CSDN博客](https://blog.csdn.net/fromVillageCoolBoy/article/details/134412425) ``` adb root adb install -r apk文件的位置 如:adb install -r E:\lingyun\RK3568\rk3568_app1.0\rk3568.apk 出现:Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.example.serial signatures do not match newer version; ignoring!表示系统中已经安装过这个软件,需要把之前的卸载后再重新安装 ``` ``` adb remount adb pull /data/system/packages.xml 拉取了packages.xml后,打开该文件将涉及com.example.serial的地方都删除 adb push packages.xml /data/system adb uninstall com.example.serial adb remount adb install -r -g E:\lingyun\RK3568\rk3568_app1.0\rk3568.apk ``` ![install_apk_method1](images/install_apk_method1.png) **方法二**: ``` adb root adb remount adb push ...\rk3568.apk /system/app adb shell chmod 666 /system/app/rk3568.apk adb reboot 重启开启APP adb shell am start -n com.example.serial/.MainActivity ``` com.example.serial是包名 ![install_apk_method2](images/install_apk_method2.png) **关闭APP** ``` adb shell am force-stop com.example.serial ``` ## 2. 模块测试 菜单:主要测试5个功能,分别是RS485通信,LED灯、CAN通信、蜂鸣器和ADC采样;点击对应的按钮进入相应的测试页面 ![2-Menu](images/2-Menu.png) ### 2.1 测试RS485通信 ![2.1_RS485_main](images/2.1_RS485_main.png) RS485通信的页面如上所示,可以根据需要选择不同的波特率、数据位、停止位、校验位和流控制,由于在硬件中已经固定了/dev/ttyS9为RS485通信的串口,因此在串口号的选择中只有/dev/ttyS9。 1. 打开串口 选择完串口的设置后,打开串口,若是由于权限不足则会弹出”添加权限“的提示。 ``` chmod 666 /dev/ttyS9 ``` ![2.1_RS485_open_failed](images/2.1_RS485_open_failed.png) 若是串口打开成功,则会以绿色字体显示”/dev/ttyS9 OPEN +串口设置“(其中校验位和流控制为None时,表示为0),如下所示。 ![2.1_RS485_open_sucess](images/2.1_RS485_open_sucess.png) 2. 发送数据 在发送数据之前,需先将串口状态转换成”发送模式“,然后在发送框中输入数据,点击”发送“即完成发送数据过程。 ![2.1_RS485_send](images/2.1_RS485_send.png) 3. 接收数据 在接收数据之前,需先将串口状态转换成”接收模式“,在发送端发送数据,然后在APP中点击”接收“按钮,就能接收到发送端发送的数据。**一次发送一次接收**。RS485是半双工通信,因此,当要发送或是接收数据时,要先改变串口的状态。 ![2.1_RS485_recv](images/2.1_RS485_recv.png) ### 2.2 测试LED灯 当按下对应的按钮后,相应的灯发生亮灭的变化,其变化同样也会显示在界面中。 例如,红灯亮起时,其上的图标也变成红色,而关闭红灯时,其上的图标变成灰色。同理,黄灯和绿灯也会发生类似的变化。 ![2.2_turn_off](images/2.2_turn_off.png) 红灯亮 ![2.2_red_turn_on](images/2.2_red_turn_on.png) ### 2.3 测试CAN通信 ![2.3_CAN_main](images/2.3_CAN_main.png) CAN通信的页面如上所示,可根据需要选择不同的can,设置发送数据帧的ID、DLC、Data。 注:在进行CAN通信前,还需进行以下设置(将can1替换成can0即可操作can0) ``` ip link set can1 down //关闭 can 网络 ip link set can1 up type can bitrate 800000 //设置 can1 的波特率为 800kbps,can 网络波特率最大值为 1mbps ip link set can1 up type can //打开 can 网络 ``` 1. 发送数据 在文本框中输入ID、DLC、Data后,点击”发送“按钮后,即可发送数据,在”发送区“会显示已发送的数据。 ![2.3_CAN_send](images/2.3_CAN_send.png) 2. 接收数据 点击”接收“按钮后,接收到的数据都会显示在”接收区“,和RS485不同,这里是”一直发送一次接收“。 ![2.3_CAN_recv](images/2.3_CAN_recv.png) ### 2.4 测试蜂鸣器 这里使用的蜂鸣器是无源蜂鸣器,用于测试pwm功能。通过设置周期和占空比可以让蜂鸣器发声。 若是由于权限问题无法打开PWM,则会弹出”添加export权限“的提示 ``` chmod 777 /sys/class/pwm/pwmchip2/* ``` ![2.4_add_export_permission](images/2.4_add_export_permission.png) 继续运行出现”添加pwm0权限”的提示 ``` chmod 777 /sys/class/pwm/pwmchip2/pwm0/* ``` ![2.4_add_pwm0_permission](images/2.4_add_pwm0_permission.png) 如下所示,设置周期为1000000,周期50000,点击”播放“,成功运行出现弹窗提醒 ![2.4_PWM_play](images/2.4_PWM_play.png) 暂停播放 ![2.4_PWM_stop](images/2.4_PWM_stop.png) ### 2.5 测试ADC采样 ADC的串口设置和RS485类似,只不过ADC通信中所使用的是十六进制数据。 串口设置:波特率(115200),数据位(8),停止位(1),校验位(None),流控制(None) ![2.5_open_ADC](images/2.5_open_ADC.png) 打开串口后,在操作设置中选择电流或电压,选择要读取数据的通道。 例如,读取第0通路的电流 ![2.5_get_current_channel0](images/2.5_get_current_channel0.png) 读取第0通道的电压 ![2.5_get_voltage_channel0](images/2.5_get_voltage_channel0.png) 其他通路的操作和上面类似 # 二、各模块接口函数说明 每个模块的功能由四个文件共同组成: 1. xxxActivity.java UI界面的功能实现。位于:\app\src\main\java\com\example\serial\xxxActivity.java 2. xxxControl.java API函数声明。位于:\app\src\main\java\com\example\serial\xxxControl.java 3. xxx_control.cpp API函数实现。位于:\app\src\main\cpp\xxx_control.cpp 4. activity_xxx.xml UI界面设计。位于:\app\src\main\res\layout\activity_xxx.xml ## 1. RS485 ### 1.1 RS485Activity.java \app\src\main\java\com\example\serial\RS485Activity.java 实现RS485通信界面中各个按钮的跳转,数据的获取。 ### 1.2 RS485Control.java \app\src\main\java\com\example\serial\RS485Control.java 1. 打开串口并设置串口的波特率、数据位、校验位、停止位、流控制。 ```java public static native int openSerialPort(String path, long baudRate, int dataBits, int parity, int stopBits, int flowControl, int maxLen); ``` path: 串口地址(/dev/ttyS9),baudRate:串口的波特率,dataBits:数据位,parity:校验位,stopBits:停止位,flowControl:流控制(通常设置为0,表示None)。**成功返回0,失败返回负数, 打开串口的权限不足返回1**。 2. 关闭串口 ```java public static native int closeSerialPort(); ``` 成功返回0,失败返回-1。 3. 发送数据 ```java public static native int sendToPort(String msg, int len); ``` msg:发送的数据,String类型;len:发送数据的长度。成功返回>0,失败返回<0。 4. 接收数据 ```java public static native String recvFromPort(int len, int timeout); ``` len:表示接收数据的长度;timeout:超时时间,单位为ms。**成功返回接收到的数据,失败返回NULL**。 5. 修改串口状态 ```java public static native int changeState(int state); ``` RS485是半双工通信,发送和接收取决于gpiochip4 26引脚的高低电平,设置为低电平发送,高电平接收。成功返回0,失败返回-1. ### 1.3 rs485_control.cpp \app\src\main\cpp\rs485_control.cpp。此文件包含RS485Control.java中函数的实现。 1. 串口结构体: ```C typedef struct comport_s { char devname[12]; unsigned int databit, parity, stopbit, flowctrl; long baudrate; int fd; int frag_size; }comport_t; ``` 2. 获取波特率 ```C static speed_t getBaudRate(int baudRate); ``` 将int类型的波特率转换成speed_t类型。 包含:0、50、75、110、134、150、200、300、600、1200、1800、2400、4800、9600、19200、38400、57600、115200、230400、460800、500000、576000、921600、1000000、1152000、1500000、2000000、2500000、3000000、3500000、4000000;若是baudRate不在这个范围内,则返回-1。 (有关RS485的UI设计位于\app\src\main\res\layout\activity_rs485.xml) ## 2. LED ### 2.1 LedActivity.java 实现按钮的功能 ### 2.2 LedControl.java ```Java public static native int ledCtrl(int which, int status); ``` which:表示要操作的灯的位置,取值为0,1,2;status:表示灯的状态,0表示开灯,1表示关灯。(低电平点亮,高电平关闭)。成功返回0,失败返回一个小于0的负数。 ### 2.3 led_control.cpp ```C enum { LED_R=0, LED_Y, LED_G, LED_MAX, }; typedef struct led_gpio_s { int idx; int gpio; const char *desc; struct gpiod_line *line; }led_gpio_t; ``` 定义结构体,gpio表示引脚编号,如红灯对应的是gpiochip0 18,那么gpio=18;desc:表示对该引脚的描述,可以表示为”red“; ```C led_gpio_t leds[LED_MAX] = { {LED_R, 18, "red", NULL}, {LED_Y, 22, "yellow", NULL}, {LED_G, 20, "green", NULL}, }; ``` 预先定义数组用来存放每个灯所对应的信息。 ## 3. CAN ### 3.1 CanActivity.java UI界面的功能实现 ### 3.2 CanControl.java 1. 发送数据 ```java public static native int sendMessage(String id, String dlc, String data, String ifname); ``` 待发送的CAN帧由id,dlc,data三部分组成,ifname表示can设备的名称(can0或can1)。成功返回0,失败返回小于0的负数。 2. 接收数据 ```java public static native String receiveMessage(String ifname); ``` ​ ifname表示can设备的名称(can0 或can1).成功返回接收到的数据,失败返回NULL。返回的数据格式为”ID=xx DLC=xx Data=xx xx xx xx“。 ### 3.3 can_control.cpp CanControl.java中的函数实现 ## 4. PWM-蜂鸣器 ### 4.1 BuzzerActivity.java UI界面的功能实现 ### 4.2 BuzzerControl.java 1. 打开PWM ```java public native static int pwmOpen(String id); ``` 打开需要操作的pwm设备,例如需要操作的pwm为: /sys/class/pwm/pwmchip2,因此id=”2“,需要操作的是/sys/class/pwm/pwmchip2/pwm0,会先判断pwm0是否存在,不存在就会导入。成功返回0,失败返回小于0的负数,打开设备的权限不足返回1. 2. 配置PWM ```java public native static int pwmConfig(String attr, String val); ``` ​ 以/sys/class/pwm/pwmchip2为例,通过配置/sys/class/pwm/pwmchip2/pwm0的period和duty_cycle可以让该PWM输出不同的方波。attr表示需要配置的文件名(period或duty_cycle),val表示写入该文件的值。打开设备的权限不足返回1,配置的值无效返回2. ### 4.3 buzzer_control.cpp BuzzerControl.java的函数实现 ## 5. ADC采样 ### 5.1 AdcActivity.java UI界面的功能实现 ### 5.2 AdcControl.java 1. 打开串口 ```Java public static native int openComport(String path, long baudRate, int dataBits, int parity, int stopBits, int flowControl); ``` path: 串口(/dev/ttyS6); baudRate: 波特率(115200);dataBits:数据位(8);parity:奇偶校验(0表示None,1表示奇校验,2表示偶校验);stopBits:停止位(1);flowControl:流控制(0表示无,1表示硬件流,2表示软件流)。成功返回0,失败返回小于0的负数,当返回1时表示打开串口的权限不够。 2. 关闭串口 ```Java public static native int closeComport(); ``` 成功返回0,失败返回小于0的负数。 3. 发送数据 ```java public static native int sendToPort(String operate, String channel); ``` 向串口发送数据,operate:操作电流或电压;channel:欲操作的通道。 下图是ADC串口协议,通过添加类型和通道号即可形成完整的读指令报文。 ![5.3_ADC_protocol](images/5.3_ADC_protocol.png) 4. 接收数据 ```Java public static native String recvFromPort(int len, int timeout); ``` 从串口读取数据并返回。len:表示读取数据的最大长度;timeout:超时时间(单位:ms)。成功返回读取到的数据,失败返回NULL。 根据接收到的数据即可计算其所对应的电流或电压。 设接收的数据为:AA 55 01 00 95 01 BB 71 //01 95 -> 405 单位0.01mA,即4.05mA,其中AA 55为报文头,01表示电流,00表示通道0,95 01表示通道0的电流值,BB 71表示CRC校验码。由于是低字节先发送,因此在计算电流值时,实际的顺序是 01 95 => 电流 = 0\*16^3 + 1\*16^2 + 9\*16^1 + 5\*16^0 = 405, 单位是0.01mA,最后得到电流值为4.05mA。 ### 5.3 adc_control.cpp ```cpp typedef struct comport_s { char devname[12]; unsigned int databit, parity, stopbit, flowctrl; long baudrate; int fd; int frag_size; }comport_t; ``` 串口配置结构体。 ```cpp static speed_t getBaudRate(int baudRate) ``` 将int类型的波特率转换成可以识别的波特率. ```cpp unsigned short crc_modbus(unsigned char *ptr, int len) ``` 根据十六进制值计算CRC16校验码。ptr:需要计算的十六进制字符数组;len:表示需要计算的字符长度。以读指令为例,设`unsigned char read[6] = {0xAA, 0x55, 0x01, 0x00}`,那么它的CRC校验码为`uint16_t crc = crc_modbus(read, 4)`, 完整的读指令为`read[4] = crc & 0xFF; read[5] = (crc >> 8) & 0xFF`. ```C void calculate(char *result, unsigned char *data, int len) ``` 计算ADC返回结果的数值,result用于存放返回的结果,格式为"电流/电压-通道X=....";data表示ADC串口返回的十六进制结果;len表示data的数据长度。 `openComport`, `closeComport`, `sendToPort`, `recvFromPort`的函数实现。 # 三、项目配置 ![3-gradle](images/3-gradle.png) ![3-sdk](images/3-sdk.png) ![3-minSDK](images/3-minSDK.png) ndk:`25.1.8937393` CMake:`3.22.1`, `3.18.1` 外部配置:`libgpiod`库。