【实例截图】


了解嵌入式的读者应该知道在单片机编程中串口(uart)通讯接口最常用的就是TTL和USB接口,将单片机TTL转USB就可以接入电脑查看串口数据实现电脑与单片机通讯,在Android AS下的NDK开发中讲解了Android使用TTL方式的接口收发数据,当然咱们常用的Android手机没有这样的接口,要实现手机和单片机串口通讯就可以用OTG来实现。

文件:590m.com/f/25127180-494509338-ef3f0a(访问密码:551685)

新建工程

添加工具类
在添加工具类时可能会有错误提示,只是包名错了,修改报错文件的包成自己当前工程的包名即可解决问题:

添加设备参数列表
在AndroidManifest中声明指定的USB设备,设备信息存放在resource="@xml/device_filter"

1 2 3 4 5 6 xml文件夹下命名为device_filter.xml: <?xml version="1.0" encoding="utf-8"?> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 至于这个文件里面的数据表示的是什么,将USB转TTL模块插入电脑,在设备管理器里面可以看到:

PID_2303:2303的10进制是8963,也就是:product-id=“8963”
VID_067B:067B的10进制是1659,vendor-id=“1659”
不同的模块这两个值就不一样,所以就有了这样一个列表,当然这也不全,没有包含所有的型号。

具体实现
在MainActivity里面就可以写设备获取与数据收发了:

package com.example.otgdemo;

import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.example.otgdemo.usbserial.driver.UsbSerialDriver;
import com.example.otgdemo.usbserial.driver.UsbSerialProber;
import com.example.otgdemo.usbserial.util.SerialInputOutputManager;
import com.example.otgdemo.utils.LogUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {

private UsbManager mUsbManager;         // usb设备管理
private List<DeviceEntry> mEntries;     // 串口设备列表
private static UsbSerialDriver sDriver; // 打开的串口设备
private static SerialInputOutputManager mSerialIoManager;   //数据发送、接收工具
private ExecutorService mExecutorService;   //数据读取线程管理@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 获取USB_SERVICE的管理器mUsbManager = (UsbManager)getSystemService(this.USB_SERVICE);mEntries = new ArrayList<>();mExecutorService = Executors.newSingleThreadExecutor();refreshDeviceList();
}
/*** 刷新usb设备列表*/
private void refreshDeviceList() {new AsyncTask<Void, Void, List<DeviceEntry>>() {@Overrideprotected List<DeviceEntry> doInBackground(Void... params) {Log.d("log","刷新设备列表 ...");final List<DeviceEntry> result = new ArrayList<>();Map<String, UsbDevice> deviceList = mUsbManager.getDeviceList();if (deviceList.isEmpty()) {Log.d("log","设备列表为空");} else {for (final UsbDevice device : mUsbManager.getDeviceList().values()) {final List<UsbSerialDriver> drivers = UsbSerialProber.probeSingleDevice(mUsbManager, device);Log.d("log","发现设备: " + device);if (drivers.isEmpty()) {Log.d("log","  - 空设备列表.");result.add(new DeviceEntry(null));} else {for (UsbSerialDriver driver : drivers) {Log.d("log","  + " + driver);result.add(new DeviceEntry(driver));}}}}return result;}@Overrideprotected void onPostExecute(List<DeviceEntry> result) {if (result.isEmpty()) {Toast.makeText(MainActivity.this, "没发现可用设备!", Toast.LENGTH_SHORT).show();return;}mEntries.clear();mEntries.addAll(result);sDriver = mEntries.get(0).driver;reStart();Log.d("log","停止刷新,发现" + mEntries.size() + " 个设备.");}}.execute((Void) null);
}/*** 打开串口*/
private void reStart() {if (sDriver == null) {Toast.makeText(this, "没有发现串口设备.", Toast.LENGTH_SHORT).show();} else {try {sDriver.open();sDriver.setParameters(57600, 8, UsbSerialDriver.STOPBITS_1, UsbSerialDriver.PARITY_NONE);} catch (IOException e) {LogUtils.d("设备打开错误: " + e.getMessage(), e);Toast.makeText(this, "设备打开错误: " + e.getMessage(), Toast.LENGTH_SHORT).show();try {sDriver.close();} catch (IOException e2) {// Ignore.}sDriver = null;return;}Toast.makeText(this, " 串口设备: " + sDriver.getClass().getSimpleName(), Toast.LENGTH_SHORT).show();}onDeviceStateChange();
}
/*** 重置串口*/
private void onDeviceStateChange() {stopIoManager();startIoManager();
}
/*** 关闭串口*/
private void stopIoManager() {if (mSerialIoManager != null) {LogUtils.d("Stopping io manager ..");mSerialIoManager.stop();mSerialIoManager = null;}
}/*** 打开串口*/
private void startIoManager() {if (sDriver != null) {LogUtils.d("Starting io manager ..");mSerialIoManager = new SerialInputOutputManager(sDriver, mListener);mExecutorService.submit(mSerialIoManager);//开启数据读取线程}
}
/*** Simple container for a UsbDevice and its driver.*/
private static class DeviceEntry {public UsbSerialDriver driver;DeviceEntry(UsbSerialDriver driver) {this.driver = driver;}
}/*** 数据读取回调*/
private final SerialInputOutputManager.Listener mListener = new SerialInputOutputManager.Listener() {@Overridepublic void onRunError(Exception e) {LogUtils.d("Runner stopped.");}// 数据接收的回调函数@Overridepublic void onNewData(byte[] data, int len) {}
};/*** 数据发送** @param data 要发送的数据*/
public static void sendData(byte[] data) {if (mSerialIoManager != null) {mSerialIoManager.writeAsync(data);}
}

}

以下内容无关:

-------------------------------------------分割线---------------------------------------------

因为题目要求解集无重复,即2,2,3和3,2,2应该算作同一种解,所以我们在回溯的时候应该先对candidates数组排序,然后每次只向下回溯大于等于自己的节点。

​ 观察解空间树我们发现:当某一层中第一次出现红色节点或绿色节点后,后面的节点将全变为红色,因为数组是经过排序的,任意节点后面的节点都是大于此节点的(candidates数组无重复元素),所以当出现一个红/绿色节点后,后面的节点不必再继续检查,直接剪枝即可。

剪枝后的解空间树如下:

这样看整棵解空间树就小多了,下面直接上代码:

Java版本的回溯解法代码

class Solution {

List<List<Integer>> result = new ArrayList<>();public List<List<Integer>> combinationSum(int[] candidates, int target) {Arrays.sort(candidates);dfs(candidates,target,0,new ArrayList());return result;
}public void dfs (int[] candidates, int target, int currSum, List<Integer> res) {if (currSum == target) {result.add (new ArrayList(res));return;}for (int i = 0; i < candidates.length; i++) {if (currSum + candidates[i] > target) {return;}int size = res.size();if (size==0 || candidates[i] >= res.get(size-1)) {res.add(candidates[i]);dfs(candidates, target, currSum+candidates[i],res);res.remove(size);}}
}

}
Go版本的回溯解法代码

func combinationSum(candidates []int, target int) (result [][]int) {
sort.Ints(candidates)
var dfs func(res []int, currSum int)
dfs = func(res []int, currSum int) {
if currSum == target {
result = append(result, append([]int(nil), res…))
return
}
for i := 0; i < len(candidates); i++ {
if currSum + candidates[i] > target {
return
}
if len(res) == 0 || candidates[i] >= res[len(res)-1] {
length := len(res)
res = append(res, candidates[i])
dfs(res, currSum+candidates[i])
res = res[:length]
}
}
}
var res []int
dfs(res, 0)
return
}
组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

示例 1:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-ii

和组合总和I不同的是这个题目中的candidates数组中出现了重复数字,而且每个数字只能使用一次,我们对这个数组进行排序,每次回溯进下一层的时候都从上一层访问的节点的下一个开始访问。画出的解空间树如下:

观察解空间树发现还是有重复的解出现,比如1,2,2出现了两次,这种问题我们可以通过两种方法来解决

每次当找到一个可行解后,判断看是否此解已经存在于之前发现的解中了,如果存在就丢弃

剪枝,同一层中同样的节点只能出现一次,这样不但整个解空间树会小很多,而且避免了判断时候的开销,下面是剪枝后的解空间树

具体剪枝的方法我们可以通过增加一个visit集合,记录同一层是否出现过相同节点,如果出现过就不再次访问此节点。

我对两种解法做了对比,执行的时间效率对比如下:第一种对应上面的结果,第二种解法对应下面的结果

下面贴出第二种解法的代码:

Java版本的回溯解法代码

class Solution {

public static void trace(List<List<Integer>> result, List<Integer> res, int[] candidates, int target, int curr, int index) {if (curr == target) {//得到预期目标result.add(new ArrayList<>(res));}Set<Integer> visit = new HashSet<>();for (int j = index+1; j < candidates.length; j++) {if (visit.contains(candidates[j])) {continue;} else {visit.add(candidates[j]);}if (curr + candidates[j] > target){//此路不通,后路肯定也不通break;} else {//继续试res.add(candidates[j]);int len = res.size();trace(result, res,candidates,target,curr+candidates[j],j);res.remove(len-1);}}
}public List<List<Integer>> combinationSum2(int[] candidates, int target) {List<Integer> res = new ArrayList<>();List<List<Integer>> result = new ArrayList<List<Integer>>();int curr = 0;Arrays.sort(candidates);trace(result, res,candidates,target,curr,-1);return result;
}

}
Go版本的回溯解法代码

func combinationSum2(candidates []int, target int) (result [][]int) {
sort.Ints(candidates)
var dfs func(res []int, currSum, index int)
dfs = func(res []int, currSum, index int) {
if currSum == target {
result = append(result, append([]int(nil), res…))
return
}
var set []int
for i := index+1; i < len(candidates); i++ {
if isExist(set, candidates[i]) {
continue
} else {
set = append(set, candidates[i])
}

     if currSum + candidates[i] > target {   //遇到红色节点,直接跳出循环,后面也无需尝试break} else {res = append(res, candidates[i])dfs(res, currSum+candidates[i], i)res = res[:len(res)-1]}}
}
var res []int
dfs(res, 0, -1)
return

}

func isExist(set []int, x int) bool {
for _, v := range set {
if v == x {
return true
}
}
return false
}
组合总和 III
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。
解集不能包含重复的组合。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-iii

此题的candidates数组不再由题目给出,而是由[1,9]区间里的数组成,且每种组合不存在重复的数,则每种数字只能用一次,我们还是继续采用回溯法,不同的是限制了解集中数字的个数。而且每层的回溯都从上一层访问的节点的下一个节点开始。

如果使用暴力法去回溯,将得到下面这样的一棵解空间树(由于树过大,所以右边被省略)

因为题目中规定了树的深度必须是k,红色表示不可能的解,绿色表示可行解,紫色表示到了规定的层数k,但总和小于n的情况。

观察上述的解空间树我们发现了剪枝的方法:

对于红色节点之后的节点直接裁剪掉
但需要注意紫色的虽然不符合题意,但由于后面可能出现正确解,所以不能剪掉
根据树的深度来剪,上面两个题中都没有规定深度,此题还可以根据深度来剪,如果超过规定深度就不继续向下探索
画出剪枝后的解空间树(同样省略了右边的树结构):

Java版本的回溯解法代码

class Solution {
public List<List> combinationSum3(int k, int n) {
List res = new ArrayList<>();
List<List> result = new ArrayList<List>();
trace(result,res,0,k,n);
return result;
}

public void trace (List<List<Integer>> result, List<Integer> res, int curr, int k, int n) {if (res.size() == k && curr == n) {result.add(new ArrayList<>(res));return;} else if (res.size() < k && curr < n) {for (int i = 1; i < 10; i++) {int len = res.size();if (len == 0 || i > res.get(len - 1)) {res.add(i);trace(result,res,curr+i,k,n);res.remove(len);}}} else {        //树的深度已经大于规定的kreturn;}
}

}
Go版本的回溯解法代码

func combinationSum3(k int, n int) (result [][]int) {
var dfs func(res []int, currSum int)
dfs = func(res []int, currSum int) {
if len(res) == k && currSum == n {
result = append(result, append([]int(nil), res…))
return
} else if len(res) < k && currSum < n {
i := 1
if len(res) > 0 {
i = res[len(res)-1]+1
}
for ; i < 10; i++ {
res = append(res, i)
dfs(res, currSum+i)
res = res[:len(res)-1]
}
} else { //搜索的深度已经超过了k
return
}
}
var res []int
dfs(res, 0)
return
}
组合总和 IV
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

示例 1:

输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。

示例 2:

输入:nums = [9], target = 3
输出:0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-iv

这个道题目并没有像上面一样要求我们找出所有的解集,而是只要求解解的个数,这时如果我们再采用回溯法去求解无疑是造成了很大的浪费,所以考虑使用动态规划,只求解个数而不关注所有解的具体内容。

题目允许数字的重复,且对顺序敏感(即不同顺序视做不同解),这样我们可以通过让每一个nums数组中数num做解集的最后一个数,这样当x作为解集的最后一个数,解集就为num1,num2,num3…x

如果dp数组的dp[x]表示target为x时候的解集个数,那么我们只需要最后求解dp[target]即可。

那么当最后一个数为x时对应的解集个数就为dp[target-x]个,让nums中的每一个数做一次最后一个数,将结果相加就是dp[target]的值,不过需要注意的是dp[0] = 1表示target为0时只有一种解法(即一个数都不要),dp的下标必须为非负数。

下面是状态转移方程(n为nums最后一个元素的下标):

dp[i]={1∑nj=0 dp[target−nums[j]i=0i!=0 && target-nums[j] > 0
Java版本的动态规划解法代码

class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target+1];
dp[0] = 1;
for (int i = 1; i <= target; i++) {
for (int num:nums) {
int tmp = i - num;
if (tmp >= 0) {
dp[i] += dp[tmp];
}
}
}
return dp[target];
}
}
Go版本的动态规划解法代码

func combinationSum4(nums []int, target int) int {
dp := make([]int, target+1)
dp[0] = 1
for i := 1; i <= target; i++ {
for _, v := range nums {
tmp := i - v
if tmp >= 0 {
dp[i] += dp[tmp]
}
}
}
return dp[target]
}

安卓Android OTG USB串口通信FT232R相关推荐

  1. Android USB 串口通信

    公司要求安卓PAD对接一台Windows的设备,实现双向数据传输. 是通过Windows设备的一根数据线进行数据传输的,涉及到的技术就是USB转串口通信,网上讲原理的一大堆,我就不讲了,直接上demo ...

  2. 安卓开发板之串口通信,通过modbus Rtu协议控制下位机

    安卓开发板之串口通信,通过modbus Rtu协议控制下位机 1.环境准备 2.编写串口操作核心类 3.编写测试类 前言:因为公司最近有个人脸识别门禁的项目,这个项目主要业务是实现远程人脸注册,管理员 ...

  3. android usb arduino,Arduino Android OTG USB

    Arduino Android OTG USB 介绍 Arduino Android OTG USB This helps you to connect any arduino to android ...

  4. 安卓设备通过USB串口与STM32单片机通讯之二

    安卓设备通过USB串口与STM32单片机通讯之二 本博文系JGB联合商务组的原创作品,引用请标明出处. 本博文接续上一篇的末尾章节. (二) APP的JAVA代码部分(使用Android Studio ...

  5. 通过python实现安卓手机与stm32串口通信

    一.材料 (1)安卓终端1台,本文使用的魅蓝手机 (2)stm32微控制器(可以串口通信的) 二.通信内容 上位机发送AA,熄灭stm32上的LED灯 上位机发送BB,点亮stm32上的LED灯 上位 ...

  6. python做上位机读usb数据_使用python实现win10系统和arduino usb串口通信

    前言 arduino有很多可以直接使用的传感器,可以很方便的获取大量的环境数据. 常见的有:温度(室温,冰箱,水箱等场景) 湿度(房间空气湿度,花盆土壤湿度等场景) 声(声控灯,噪音检测,深海测量,医 ...

  7. 树莓派串口通信 USB串口通信 常用串口命令

    树莓派串口通信 USB串口通信 文章目录 树莓派串口通信 USB串口通信 准备 发送数据 接收数据 如果不知道树莓派怎么使用USB串口通信的,欢迎浏览我的另一篇博客文章: 树莓派使用USB串口通信 C ...

  8. android c语言串口通信,安卓串口通信能用的modebus CRC16计算,附对应的C语言CRC16

    最近写安卓串口通信需要用到modebus 的crc16通信,网上找了很多都是字符串的,我需要byte的,由于java没有无符号数,转换有点小麻烦,对着C一步步测试,最终测试无误 代码如下: //jav ...

  9. 安卓USB串口通信 Arduino

    OkUSB,一个简洁的Android串口通信框架. 功能简介 支持设置波特率 支持设置数据位 支持设置停止位 支持设置校验位 支持DTS和RTS 支持串口连接状态监听 文件:url80.ctfile. ...

最新文章

  1. numpy中ravel函数、flatten函数的功能及差异
  2. WordCount结对拓展
  3. android builder模式 插件,如何在Kotlin中实现Builder模式?
  4. undefined: resolver.BuildOption undefined: resolver.ResolveNowOption 报错的解决办法
  5. 在 SAP BTP Kyma Runtime 上使用 Redis 读取和存储数据
  6. php ftp 创建文件夹失败,PHP使用FTP函数创建目录
  7. 引用内部函数绑定机制,R转义字符,C++引用,别名,模板元,宏,断言,C++多线程,C++智能指针...
  8. 怎么在HTML图片中加文字,html+css怎么在图片上添加文字
  9. 测试tf卡读写速度软件,U盘测速图文教程,优盘读写速度测试,移动硬盘TF卡SD内存卡测速...
  10. win10建立符号链接
  11. 信息学奥赛 java C 选_信息学奥赛(NOIP)入门指南(新家长必读)
  12. win7访问linux共享没有权限设置,局域网共享时提示:你没有权限访问,请与网络管理员联系...
  13. 谈谈自我介绍与第一印象
  14. SpringBoot 项目(若依脚手架)2
  15. 百篇已过,又是一个新篇章,谈谈感受吧
  16. Python模糊匹配 | 刷英语六级段落匹配只需要3秒?
  17. 服务网格领域的百花齐放
  18. 园区网的网关部署在接入层还是汇聚层 面试官与求职者之间谈话
  19. 安卓开发与ios开发!一个Android应届生从上海离职,复习指南
  20. 智能电话机器人介绍(AI语音机器人)

热门文章

  1. iphone计算机快捷键,PC 上的 iTunes 键盘快捷键
  2. 【解决】华为Mate20开发者模式自动关闭USB调试【Android Studio经常发现不了Mate20也是这个问题】
  3. B站榜单丨B站UP主行业排行榜周榜2021年7月5日-7月11日
  4. 佳能ip110 linux驱动下载,佳能Canon iP110 驱动
  5. 2015年阿里巴巴校招研发工程师在线笔试题汇总
  6. IDEA创建项目没有Java选项
  7. 锐龙R5 4500U和锐龙R7 3700U 的区别
  8. Linux 运行python命令
  9. 添加机构organizations模块
  10. 莫氏硬度6级以上,手机玻璃其实很硬【转】