安卓USB硬件通信,监控电池的电量信息
1.新建一个类来获取设备信息和匹配设备
public class UartVCP {
private final String TAG = "UartVCP";
UsbDevice device = null;
int stm32VID = 0x0483, stm32PID = 0x5740;
private UsbSerialPort port;
public void InitUartVCP(UsbManager manager){
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Log.e(TAG, "get device list = " + deviceList.size());
//遍历当前的设备列表
for (UsbDevice usbDevice : deviceList.values()) {
device = usbDevice;
Log.d(TAG, "vid: " + device.getVendorId() + "\t pid: " + device.getProductId());
if (device.getVendorId() == stm32VID && device.getProductId() == stm32PID) {
break;
}
}
// 判断设备的ID
if(device!=null && device.getVendorId()==stm32VID && device.getProductId()==stm32PID){
getUsbInfo(device);
}
else{
Log.d(TAG,"Don't find desired device.");
}
// Find all available drivers from attached devices.
// 从链接的设备种查找所有可用的usb串行驱动程序,并尝试与指定的设备链接,
List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
//通过USB序列号检测器获取当前链接的所有USB串行驱动程序列表
for (final UsbSerialDriver driver : availableDrivers) {
// 对于每个驱动程序,检查是否与目标设备匹配,如果匹配则进行下一步操作
if(driver.getDevice().equals(device)){
//获取端口列表的第一个端口
port = driver.getPorts().get(0);
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
if (connection == null) {
//无法打开连接,可能需要请求权限
// add UsbManager.requestPermission(driver.getDevice(), ..) handling here
return;
}
try {
//使用打开的连接,打开端口
port.open(connection);
//设置端口的参数(波特率,数据位,停止位,校验码)
port.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
//发生数据
public void sendData(byte[] data) {
try {
Log.d(TAG, "data = " + bytesToHexString(data));
if (null != port){
port.write(data, 100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//读取数据
public int readData(byte[] response) {
int len = 0;
try {
if (null != port){
len = port.read(response, 100);
}
Log.d(TAG, "len = " + len + " response = " + bytesToHexString(Arrays.copyOfRange(response, 0, len)));
} catch (Exception e) {
e.printStackTrace();
}
return len;
}
//将字节数组转换成十六进制的字符串
public String bytesToHexString(byte[] bArr) {
StringBuilder sb = new StringBuilder(bArr.length);
String sTmp;
for (byte b : bArr) {
sTmp = Integer.toHexString(0xFF & b);
if (sTmp.length() < 2)
sb.append(0);
sb.append(sTmp.toUpperCase());
}
return sb.toString();
}
private void getUsbInfo(UsbDevice usbDevice){
StringBuilder sb = new StringBuilder();
if(Build.VERSION.SDK_INT >= 23){
sb.append(String.format("VID:0x%04X PID:0x%04X ManuFN:%s PN:%s V:%s; DeviceName:%s",
usbDevice.getVendorId(),
usbDevice.getProductId(),
usbDevice.getManufacturerName(),
usbDevice.getProductName(),
usbDevice.getVersion(),
usbDevice.getDeviceName()
));
}
else {
sb.append(String.format("VID:%04X PID:%04X ManuFN:%s PN:%s",
usbDevice.getVendorId(),
usbDevice.getProductId(),
usbDevice.getManufacturerName(),
usbDevice.getProductName()
));
}
Log.d(TAG, "Find my STM32 USB Serial Device ~ " + sb.toString());
}
}
2.在activity中使用
//热插拔,如果有的话就去回调下边的代码,监听在哪里就在哪里回调
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
RCApplication.uartVCP.InitUartVCP(manager);
// 广播监听热插拔
UsbBroadcastReceiver();
byte[] data = RequestIPC.batteryRequest();
RCApplication.uartVCP.sendData(data);
byte[] response = new byte[100];
int len = RCApplication.uartVCP.readData(response);
RCApplication.replyIPC.ipc_put_rx_byte(response, len);
3.定义数据处理基类
public abstract class HandlerInit {
public HeaderV1 header;//存储数据包头信息
public short crc16;
public HandlerInit() {
header = new HeaderV1();
//帧的起始标志
header.sof = Unpack.START_OF_FRAME_BYTE;
//校验和
header.checksum = (byte)(header.sof + header.ver_len);
//帧类型 设置为命令帧类型
header.status = Unpack.FRAME_TYPE_CMD;
//发送方标识,设置为IPC设备标识
header.sender = Unpack.DEVICE_IPC << 3 | Unpack.INDEX_IPC;
/* 扩展修改点二: 上面的已固定不用更改,从这里开始,接收方可能会不同,需要根据命令调整 */
header.receiver = Unpack.DEVICE_MCU << 3 | Unpack.INDEX_MCU;
/* 命令对应指令集 */
header.cmd_set = Unpack.IPC_CMD_SET;
}
/**
* 用于处理数据,并填充头部信息
* @param struct 数据结构数组,用于存储数据
* @param cmdLength 命令长度
* @param cmdID 命令ID
*/
public void handlerProcess(byte[] struct, int cmdLength, byte cmdID) {
header.ver_len = (short)(Unpack.FRAME_PROTOCOL_V1 << 10 | cmdLength);
/* 具体的指令ID */
header.cmd_id = cmdID;
HeaderV1.headerTransfer(header, struct);
}
}
4.定义请求电池数据类
public static class BatteryRequest extends HandlerInit {
private final int thisLength = getMyLength(getClass());
private byte[] struct = new byte[thisLength];
public byte[] setRequest(){
handlerProcess(struct, thisLength, Unpack.BATTERY_REQUEST_CMD);
String TAG = "BatteryRequest";
Log.d(TAG, "length = " + thisLength);
CRC16.formatFrameBuffer(struct);
return struct;
}
}
public static byte[] batteryRequest() {
BatteryRequest request = new BatteryRequest();
return request.setRequest();
}
5.根据数据恢复解析电池数据
public class ReplyIPC {
final byte SOF_DETECTED = 0; /* 起始标志检测 */
final byte HEADER_DETECTED_1 = 1; /* 帧头检测 */
final byte HEADER_DETECTED_2 = 2; /* 帧头检测 */
final byte HEADER_DETECTED_3 = 3; /* 帧头检测 */
final byte DATA_RECEIVED = 4; /* 数据接收 */
static byte unpack_step = 0;
static int head_index = 0;
static int tail_index = 0;
static byte[] receiveBuffer = new byte[256];
static byte[] unpack_buf = new byte[100];
static int unpack_buf_idx = 0;
static int unpack_len = 0;
public BatteryReply batteryReply = new BatteryReply();
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
//将接收到的字节数据存储到接收缓冲区
public void ipc_put_rx_byte(byte[] data, int len) {
lock.writeLock().lock();
for (int i=0; i<len; i++) {
receiveBuffer[head_index] = data[i];
head_index = (head_index + 1) % 256;
}
lock.writeLock().unlock();
}
//从接收缓冲区读取一个字节的数据
public short ipc_get_rx_byte() {
short data = 0;
if(tail_index != head_index)
{
lock.readLock().lock();
data = receiveBuffer[tail_index];
tail_index = (tail_index + 1) % 256;
lock.readLock().unlock();
return data;
}else {
return 0x1fff;
}
}
public static int byteToInt(byte b) {
//Java 总是把 byte 当做有符处理;我们可以通过将其和 0xFF 进行二进制与得到它的无符值
return b & 0xFF;
}
public void getFullData() {
short data = ipc_get_rx_byte();
if (data == 0x1fff)
return;
switch (unpack_step){
//检测帧的起始标志
case SOF_DETECTED:
if (data == Unpack.START_OF_FRAME_BYTE) {
// 将缓冲区的索引置为0,并将起始标志存储到缓冲区域。然后更新解析步骤
unpack_buf_idx = 0;
unpack_buf[unpack_buf_idx] = (byte)data;
unpack_buf_idx ++;
unpack_step = HEADER_DETECTED_1;
}
break;
//
case HEADER_DETECTED_1:
//将接收到的数据存储到解析缓冲区中,并将解析长度设置为接收到的数值,然后更新解析步骤
unpack_buf[unpack_buf_idx] = (byte)data;
unpack_len = data;
unpack_step = HEADER_DETECTED_2;
unpack_buf_idx++;
break;
case HEADER_DETECTED_2:
// 将解析到的数据存储到解析缓冲区中,并根据收到的数据更新解析长度,如果大于100表示数据异常需要重新开始解析,否则更新解析步骤
unpack_buf[unpack_buf_idx] = (byte)data;
unpack_len |= (((unpack_buf[unpack_buf_idx]) & 0x03) << 8);
if((unpack_len) > 100)
{
unpack_step = SOF_DETECTED;
unpack_buf_idx = 0;
}
else
{
unpack_step = HEADER_DETECTED_3;
unpack_buf_idx++;
}
break;
case HEADER_DETECTED_3:
// 将接收到的数据存储到解析缓冲区中,并检查头校验和是否正确,如不正确表示数据异常重新解析,正常更新解析步骤
unpack_buf[unpack_buf_idx] = (byte)data;
if((unpack_buf[3]) != ((unpack_buf[0]) + (unpack_buf[1]) + (unpack_buf[2])))
{
unpack_step = SOF_DETECTED;
unpack_buf_idx = 0;
} else {
unpack_step = DATA_RECEIVED;
unpack_buf_idx++;
}
break;
case DATA_RECEIVED:
// 将接收到的数据存储到解析缓冲区中,直到接收到的数据长度达到解析长度为止,然后检查校验和是否正确,正确调用方法处理数据
if((unpack_buf_idx) < (unpack_len))
{
unpack_buf[unpack_buf_idx] = (byte)data;
unpack_buf_idx++;
}
else if((unpack_buf_idx) > (unpack_len))
{
unpack_step = SOF_DETECTED;
unpack_buf_idx = 0;
}
if (unpack_buf_idx == unpack_len)
{
unpack_step = SOF_DETECTED;
int crc16 = CRC16.calc_crc16(unpack_buf, unpack_len - 2);
if (crc16 == (((unpack_buf[unpack_len - 1] << 8) | (unpack_buf[unpack_len - 2] & 0x00FF)) & 0xFFFF))
{
frame_received_handler_v1(unpack_buf);
}
}
break;
default:
}
}
private void frame_received_handler_v1(byte[] buf)
{
// 检查数据的第11个字节
switch (buf[10]){
case Unpack.BATTERY_REPLY_CMD:
BatteryReply.current = (buf[12] << 8) | buf[11];
BatteryReply.capacity_percent = buf[13];
EventBus.getDefault().post(batteryReply);
break;
}
}
public static class BatteryReply {
private static int current; // unit:10mA
private static short capacity_percent; // unit:%
private static byte temperature; // unit:degree centigrade
private static int rated_capacitance; // unit:10mAh
private static int surplus_capacitance; //unit:10mAh
public int getCurrent(){
return current;
}
public short getCapacity_percent(){
return capacity_percent;
}
}
}