QT 绿米QTA协议 (二)AS

 几圈年轮    Android 串口通信(一)  giuhub demo 地址 https://github.com/MickJson/AndroidUSBSerialPort

一.PowerUpdate

1.1.UI

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="7"
        android:gravity="center"
        android:orientation="horizontal">

        <ScrollView
            android:id="@+id/sv_send"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="5"
            android:background="@drawable/bg_white_stroke">

            <TextView
                android:id="@+id/tv_send"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/receive_hint"
                android:padding="@dimen/margin_small"
                android:textColor="@android:color/black"
                android:textSize="@dimen/text_tv" />
        </ScrollView>

        <ScrollView
            android:id="@+id/sv_result"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="5"
            android:background="@drawable/bg_white_stroke">

            <TextView
                android:id="@+id/tv_result"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/receive_hint"
                android:padding="@dimen/margin_small"
                android:textColor="@android:color/black"
                android:textSize="@dimen/text_tv" />
        </ScrollView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.5"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_start"
            style="@style/average_vertical_widget"
            android:text="@string/start_test" />

        <Button
            android:id="@+id/btn_close"
            style="@style/average_vertical_widget"
            android:text="@string/close_port" />

    </LinearLayout>

</LinearLayout>

 styles.xml

<resources>
    <!--font Style-->
    <style name="average_vertical_widget">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:layout_weight">1</item>
        <item name="android:gravity">center</item>
        <item name="android:textSize">@dimen/text_size</item>
        <item name="android:textColor">@android:color/black</item>
    </style>

</resources>

1.2.android_serialport_api 串口工具类

package android_serialport_api;

import android.util.Log;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class SerialPort {

    private static final String TAG = "SerialPort";

    /*
     * Do not remove or rename the field mFd: it is used by native method close();
     */
    private FileDescriptor mFd;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;

    public SerialPort(File device, int baudrate, int dataBits, int stopBits, char parity) throws SecurityException, IOException {

        /* Check access permission */
        if (!device.canRead() || !device.canWrite()) {
            try {
                /* Missing read/write permission, trying to chmod the file */
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
                        + "exit\n";
                su.getOutputStream().write(cmd.getBytes());
                if ((su.waitFor() != 0) || !device.canRead()
                        || !device.canWrite()) {
                    throw new SecurityException();
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new SecurityException();
            }
        }
        mFd = open(device.getAbsolutePath(), baudrate, dataBits, stopBits, parity);
        if (mFd == null) {
            Log.e(TAG, "native open returns null");
            throw new IOException();
        }
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
    }

    // Getters and setters
    public InputStream getInputStream() {
        return mFileInputStream;
    }

    public OutputStream getOutputStream() {
        return mFileOutputStream;
    }

    public void Write_Rs485_Length(int count) {
        write_rs485_length(count);
    }

    // JNI(调用java本地接口,实现串口的打开和关闭)
    /**串口有五个重要的参数:串口设备名,波特率,检验位,数据位,停止位
     其中检验位一般默认位NONE,数据位一般默认为8,停止位默认为1*/
    /**
     * @param path     串口设备的据对路径
     * @param baudrate 波特率
     * @param dataBits 数据位
     * @param stopBits 停止位
     * @param parity   校验位
     */
    private native static FileDescriptor open(String path, int baudrate, int dataBits, int stopBits, char parity);

    public native void close();

    public native void write_rs485_length(int count);

    static {//加载jni下的C文件库
        Log.i(TAG, "loadLibrary..............gatsby");
        System.loadLibrary("serial_port");
    }
}

 二.qtcreator  -> AS

2.1.UartTest

package com.lvmi.powerupdate;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android_serialport_api.SerialPort;

public class UartTest {

    private final Context mContext;
    byte[] Save_Data;                // 保存的载入数据
    byte[] Temp_file_Data;           // 临时文件数据
    byte[] file_arry;                // 文件数据
    byte[] Send_Data;                // 发送数据
    byte Data_Number;                 // 数据编码
    int flag_EOT;                    // 结束命令标记
    int file_ChkData;                // 文件校验
    int NAck_Cnt;                    // NAck次数
    int Ack_Cnt;                     // Ack次数
    private OnSerialListener mOnSerialListener;
    public String DEV_TTYS4 = "/dev/ttyS4";
    private boolean isOpened = false;
    private OutputStream mOutputStream;
    private InputStream mInputStream;
    private final ExecutorService mThreadPoolExecutor = Executors.newCachedThreadPool();

    public UartTest(Context mContext) {
        super();
        this.mContext = mContext;
    }

    public void StartUartTest() {
        openSerialPort(DEV_TTYS4);
        Btn_Open_File();
        Btn_Sand_Data();//启动
    }

    //打开文件
    public void Btn_Open_File() {
        int val;
        file_arry = getBytesByFile(mContext, "QT573-LVMI-APP.bin");

        val = file_arry.length % 128;
        if (val != 0) {
            val = 128 - val;
            //最后一包数据补24个0
            byte[] chrVal2 = new byte[val];
            for (int i = 0; i < val; i++) {
                chrVal2[i] = (byte)0;
            }
            file_arry = addBytes(file_arry, chrVal2);
            file_ChkData = Dev_CRC16(file_arry);
            //must F229
            Log.d("gatsby", "file_ChkData -> " + Integer.toHexString(file_ChkData));

        }
    }

    //启动
    public void Btn_Sand_Data() {

        // 清除 计数 接收文本 保存数据
        NAck_Cnt = 0;
        Ack_Cnt = 0;
        Save_Data = null;

        // 开始接收数据
        isOpened = true;

        // 开始发送进入编译环境
        Data_Number = 0;               // 重新发送计数
        flag_EOT = 0;                  // 发送结束标记位重置
        Sand_Data((byte) 0x93);        // 发送
    }

    //发送数据
    public void Sand_Data(byte cmd) {
        //StringBuilder ba = new StringBuilder();
        byte[] ba = new byte[133];
        int ChkData;//0xffff
        byte val;//0xff
        String sendData = null;

        if (cmd == 0x01) {

            byte[] data;

            ba[0] = cmd;
            Log.d("gatsby", "Data_Number ->" + Data_Number);
            val = (byte) ~Data_Number;
            ba[1] = (byte) Data_Number;
            ba[2] = (byte) val;

            for (int i = 0; i < Send_Data.length; i++) {
                ba[i + 3] = (byte) Send_Data[i];
            }

            //System.arraycopy(Send_Data, 0, data, 0, Send_Data.length);
            data = Send_Data;
            Log.d("gatsby", "data->" + data.length);

            // 数据校验
            ChkData = CRC16(data, 128);
            Log.d("gatsby", "ChkData->" + ChkData);

            val = (byte) (ChkData & 0xff);
            ba[Send_Data.length + 3] = (byte) val;

            val = (byte) ((ChkData >> 8) & 0xff);
            ba[Send_Data.length + 4] = (byte) val;

            //打印看发送数据是否正确
//            for (int i = 0; i < ba.length; i++) {
//                Log.d("gatsby", "i--> " + (i) + " ba ->"+ Integer.toHexString(ba[i] & 0xff));
//            }
            sendSerialPort(ba);

            if (flag_EOT == 0) {
                Log.d("gatsby", "Sand Packet " + Data_Number);
                sendData = "Send Packet " + Data_Number;
            }
            // 发送校验
            else if (flag_EOT == 1) {
                Log.d("gatsby", "Sand Check");
                sendData = "Send Check";
            }

            if (mOnSerialListener != null) {
                mOnSerialListener.onSendData(sendData);
            }
        } else {
            byte[] ba2 = new byte[1];
            ba2[0] = (byte) cmd;
            sendSerialPort(ba2);
            Log.d("gatsby", "cmd ->" + Integer.toHexString(cmd & 0xff));
            if (cmd == (byte) 0x93) {
                Log.d("gatsby", "Sand_Data will be send data ");
            } else if (cmd == (byte) 0x04) {
                Log.d("gatsby", "Sand_Data End ");
                isOpened = false;
            }
        }
    }

    public void openSerialPort(String devType) {
        if (!"".equals(devType) && new File(devType).exists()) {
            Log.d("gatsby", "isOpenedSerialPort");
            try {
                //串口配置:9600 数据位8   起始位1  停止位1   奇校验
                SerialPort mSerialPort = new SerialPort(new File(DEV_TTYS4), 9600, 8, 1, 'O');
                mOutputStream = mSerialPort.getOutputStream();
                mInputStream = mSerialPort.getInputStream();
                mThreadPoolExecutor.execute(new ReceiveDataThread());

                isOpened = true;
            } catch (IOException e) {
                e.printStackTrace();
                if (mOnSerialListener != null) {
                    mOnSerialListener.onSerialOpenException(e);
                }
            }
        }
    }

    /**
     * 串口 发送字符串
     */
    public void Sand_Data_QString(String st) {
        st += "\r\n";
        byte[] ba = st.getBytes();
        sendSerialPort(ba);
    }

    /**
     * 串口 发送byte[]
     */
    public void sendSerialPort(byte[] sendbyte) {
        if (!isOpened) {
            return;
        }
        try {
            mOutputStream.write(sendbyte);
            mOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //5s 未接收数据 提示升级失败 Android11 提示过时 换Looper写法
    Handler handler = new Handler(Looper.myLooper());
    private final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            if (Data_Number == 1) {
                if (mOnSerialListener != null) {
                    mOnSerialListener.onUpdateError();
                }
            }
        }
    };

    /**
     * 串口返回数据内容读取
     */
    private class ReceiveDataThread extends Thread {
        @Override
        public void run() {
            super.run();
            int val;//uint8_t
            String SendStr = null;
            String ReciveStr = null;
            boolean start_flag = false;
            byte[] ba = new byte[1024];
            while (isOpened) {
                String resSaveData;
                if (mInputStream != null) {
                    try {
                        int size = mInputStream.read(ba);
                        if (size > 0) {
                            resSaveData = new String(ba, 0, size);
                            Log.d("gatsby", "receiver str = " + resSaveData);
                            Save_Data = resSaveData.getBytes();
                            if (resSaveData.equals("Apply OTA\r\n")) {
                                Log.d("gatsby", "Apply OTA");
                                SendStr = "OTA";
                                start_flag = true;
                                ReciveStr = "Apply OTA";
                                Sand_Data_QString(SendStr);
                            } else if (resSaveData.equals("Ready\r\n")) {
                                Log.d("gatsby", "Ready");
                                SendStr = "Start";
                                ReciveStr = "Ready";
                                Sand_Data_QString(SendStr);
                                NAck_Cnt = 0;
                                Ack_Cnt = 0;
                                Data_Number = 0;        // 重新发送计数
                                flag_EOT = 0;           // 发送结束标记位重置
                                Temp_file_Data = file_arry;
                            }
                            // NACK
                            else if (Save_Data[0] == 0x15) {
                                NAck_Cnt++;
                                if (Data_Number == 0) {
                                    Data_Number = 1;
                                    // 首次发送
                                    Log.d("gatsby", "(NAck) Ready Receive");
                                    ReciveStr = "(NAck) Ready Receive";
                                    Send_Data = cutBytes(Temp_file_Data);
                                    Sand_Data((byte) 0x01);
                                } else if (Data_Number > 0) {
                                    Log.d("gatsby", "(NAck) Ready Packet Error");
                                    ReciveStr = "(NAck) Ready Receive";
                                    Send_Data = cutBytes(Temp_file_Data);
                                    Sand_Data((byte) 0x01);
                                }
                                handler.postDelayed(runnable, 6000);
                            }
                            // ACK
                            else if (Save_Data[0] == 0x06) {
                                Ack_Cnt++;
                                Data_Number++;
                                //Log.d("gatsby", "(Ack) Receive ->" + Save_Data[0]);
                                //Log.d("gatsby", "flag_EOT->" + flag_EOT);
                                Log.d("gatsby", "Temp_file_Data aaa->" + Temp_file_Data.length);
                                if (Temp_file_Data.length > 128) {

                                    Temp_file_Data = removeBytes(Temp_file_Data, Temp_file_Data.length - 128);
                                    Log.d("gatsby", "Temp_file_Data bbb->" + Temp_file_Data.length);
                                    // 发送数据
                                    Send_Data = cutBytes(Temp_file_Data);
                                    if (Temp_file_Data.length == 128) {
                                        Temp_file_Data = removeBytes(Temp_file_Data, 0);
                                    }
                                    Sand_Data((byte) 0x01);
                                    Log.d("gatsby", "(Ack) Receive Packet " + (Data_Number - 1) + " Success");
                                    ReciveStr = "(Ack) Receive Packet " + (Data_Number - 1) + " Success";
                                } else {
                                    // 发送校验
                                    if (flag_EOT == 0) {
                                        flag_EOT = 1;
                                        val = (byte) (file_ChkData & 0xff);
                                        Send_Data[0] = (byte) val;
                                        val = (byte) ((file_ChkData >> 8) & 0xff);
                                        Send_Data[1] = (byte) val;
                                        for (int i = 0; i < 126; i++) {
                                            Send_Data[i + 2] = (byte) 0;
                                        }
                                        Sand_Data((byte) 0x01);
                                        Log.d("gatsby", "EOT (Ack) Receive Packet " + (Data_Number - 1) + " Success");
                                        ReciveStr = "(Ack) Receive Packet " + (Data_Number - 1) + " Success";
                                    }
                                    // 发送EOT 完成
                                    else if (flag_EOT == 1) {
                                        flag_EOT = 2;
                                        Sand_Data((byte) 0x04);
                                        Log.d("gatsby", "(Ack) Receive Check");
                                        ReciveStr = "(Ack) Receive Check";
                                    }
                                }
                            }
                            //设置监听
                            if (mOnSerialListener != null) {
                                mOnSerialListener.onReceivedData(ReciveStr);
                                if (start_flag) {
                                    mOnSerialListener.onSendData(SendStr);
                                    if (SendStr.equals("Start")) {
                                        start_flag = false;
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 关闭串口
     */
    public void closeSerialPort() {
        try {
            if (mOutputStream != null)
                mOutputStream.close();
            if (mInputStream != null)
                mInputStream.close();
            isOpened = false;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置串口监听
     *
     * @param onSerialListener 串口监听
     */
    public void setOnSerialListener(OnSerialListener onSerialListener) {
        this.mOnSerialListener = onSerialListener;
    }

    /**
     * 串口监听
     */
    public interface OnSerialListener {

        /**
         * 串口打开异常
         */
        void onSerialOpenException(Exception e);

        /**
         * 串口数据发送
         */
        void onSendData(String sendData);

        /**
         * 串口数据返回
         */
        void onReceivedData(String receivedData);


        /**
         * 上位机下发后若5S未接收MCU数据 上位机退出升级过程
         */
        void onUpdateError();

    }

    /**
     * CRC校验 128字节
     */
    public int CRC16(byte[] u8pArray, int u8Size) {
        int val;//0xff
        int i, j;
        int CurVal;//0xffff
        int CrcReg = 0xFFFF;

        for (i = 0; i < u8Size; i++) {
            //CurVal = u8pArray[i] << 8;  uint8_t
            val = u8pArray[i] & 0xff;
            CurVal = val << 8;

            for (j = 0; j < 8; j++) {
                if ((short) (CrcReg ^ CurVal) < 0) {
                    CrcReg = (((CrcReg << 1) & 0xffff) ^ 0x1021);
                } else {
                    CrcReg = (CrcReg << 1) & 0xffff;
                }
                CurVal <<= 1;
            }
        }
        return CrcReg;
    }

    /**
     * CRC校验
     */
    public int Dev_CRC16(byte[] ba) {
        int val;//0xff
        int i, j;
        int CurVal;//0xffff
        int CrcReg = 0xFFFF;

        for (i = 0; i < ba.length; i++) {
            //uint8_t -> int
            val = ba[i] & 0xff;
            CurVal = val << 8;

            for (j = 0; j < 8; j++) {
                if ((short) (CrcReg ^ CurVal) < 0) {
                    CrcReg = (((CrcReg << 1) & 0xffff) ^ 0x1021);
                    //Log.d("gatsby","file_arry i-> " + i + " CrcReg -> " + CrcReg);
                } else {
                    CrcReg = (CrcReg << 1) & 0xffff;
                }
                CurVal <<= 1;
            }
        }
        return CrcReg;
    }

    /**
     * 两个字节数组合并
     */
    public byte[] addBytes(byte[] firstData, byte[] secondData) {
        byte[] mergeData = new byte[firstData.length + secondData.length];
        System.arraycopy(firstData, 0, mergeData, 0, firstData.length);
        System.arraycopy(secondData, 0, mergeData, firstData.length, secondData.length);
        return mergeData;
    }

    /**
     * 字节数组 截取首128字节 128
     */
    public byte[] cutBytes(byte[] Data) {
        byte[] cutData = new byte[128];
        System.arraycopy(Data, 0, cutData, 0, cutData.length);
        return cutData;
    }

    /**
     * 字节数组 移除首128字节 128
     */
    public byte[] removeBytes(byte[] Data, int removeLength) {
        byte[] removeData = new byte[removeLength];
        System.arraycopy(Data, 128, removeData, 0, removeData.length);
        return removeData;
    }

    /**
     * 获取assets文件流 转byte数组
     */
    public byte[] getBytesByFile(Context context, String fileName) {
        try {
            InputStream inStream = context.getResources().getAssets().open(fileName);
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }
            outStream.close();
            inStream.close();
            return outStream.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

 MainActivity  回调刷新UI 数据

package com.lvmi.powerupdate;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements View.OnClickListener, UartTest.OnSerialListener {

    private ScrollView  sv_send,sv_result;
    private Button btn_start, btn_close;
    private TextView tv_Send, tv_Result;
    private UartTest uartTest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();
        initView();
    }

    private void initData() {
        if (uartTest == null) {
            uartTest = new UartTest(this);
            uartTest.setOnSerialListener(this);
        }
    }

    private void initView() {
        sv_send = (ScrollView) findViewById(R.id.sv_send);
        sv_result = (ScrollView) findViewById(R.id.sv_result);
        tv_Send = (TextView) findViewById(R.id.tv_send);
        tv_Result = (TextView) findViewById(R.id.tv_result);
        btn_start = (Button) findViewById(R.id.btn_start);
        btn_start.setOnClickListener(this);
        btn_close = (Button) findViewById(R.id.btn_close);
        btn_close.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_start:
                tv_Send.setText("");
                tv_Result.setText("");
                btn_start.setEnabled(false);
                uartTest.StartUartTest();
                break;
            case R.id.btn_close:
                uartTest.closeSerialPort();
                btn_start.setEnabled(true);
                break;
        }
    }

    @Override
    public void onSerialOpenException(Exception e) {
        showData("串口打开失败",tv_Send);
    }

    @Override
    public void onSendData(String sendData) {
        showData(sendData, tv_Send);
        if(sendData.equals("Send Check")){
            showData("已成功发送完数据包 ", tv_Send);
            btn_start.setEnabled(true);
        }
        sv_send.fullScroll(ScrollView.FOCUS_DOWN);
    }

    @Override
    public void onReceivedData(String receivedData) {
        showData(receivedData, tv_Result);
        if(receivedData.equals("(Ack) Receive Check")){
            showData("已接受完数据,升级成功 ", tv_Result);
        }
        sv_result.fullScroll(ScrollView.FOCUS_DOWN);
    }

    @Override
    public void onUpdateError() {
        showData("未接收MCU数据,升级失败", tv_Result);
    }

    private void showData(final String str, TextView tv) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tv.append(str + "\n");
            }
        });
    }

}

  

posted @ 2022-01-15 14:58  CrushGirl  阅读(76)  评论(0编辑  收藏  举报