笔记89 | Canbus升级程序指南

最近有负责canbus升级程序项目,现在来总结一下,升级就是将数据发送给can盒子的一个过程,目前已经有调试好欣扑,比纳瑞,威驰,福嘉太四个公司的升级程序,总结来说就分以下几步:

  1. 扫描本地内存目录,找升级文件
  2. 进入RESET模式
  3. 校验一些数据密码等
  4. 发送数据
  5. 接受是否升级成功

1,扫描本地内存目录,找升级文件

为方便客户升级,我们定了将升级文件拷至U盘根目录,插到机器USB接口,点击我们的升级程序就可以进行升级

第一步就是要找到升级文件

private void getUSBFilePath() {
        try {
            // 各个平台之间路径不一样
            if(files == null){
                files = new File("/storage").listFiles();
            }

            if(files == null || files.length ==0){
                files = new File("/mnt/usbhost").listFiles();
            }

            if(files == null || files.length ==0){
                files = new File("/mnt/storage").listFiles();
            }

            for (final File file : files) {
                String ss = file.getName();
                Log.i(TAG, "name:"+ss);
                boolean     isUsb = file.getName().startsWith("usb");
                boolean isStorage = file.getName().startsWith("Storage");
                if (file.canRead() && file.isDirectory() && isUsb) {
                    //满足该条件的文件夹就是u盘在手机上的目录 返回抽象路径名的绝对路径名字符串
                    path = file.getAbsolutePath();
                    Log.i(TAG, "usb升级文件路径:"+path);
                }
                if (file.canRead() && file.isDirectory() && isStorage) {
                    //满足该条件的文件夹就是u盘在手机上的目录 返回抽象路径名的绝对路径名字符串
                    path = file.getAbsolutePath();
                    Log.i(TAG, "storage升级文件路径:"+path);
                }
            }
            File[] fileUSB = new File(path).listFiles(new FileFilter() {

                @Override
                public boolean accept(File f) {
                    String n = f.getName();
                    if(n.endsWith(".hex") || n.endsWith(".UPDE") || n.endsWith(".iap") || n.endsWith(".bin")) {
                        return true;
                    }
                    return false;
                }

            });
            if(fileUSB != null) {
                for(File f : fileUSB) {
                    path = f.getAbsolutePath();
                     if(path.contains(".hex") && path.contains("BNR")){ //比纳瑞
                        Intent mIntent =  new Intent(this,BNRActivity.class);
                        mIntent.putExtra("PATH", path);
                        startActivity(mIntent);
                        finish();
                        return;
                    }else if(path.contains(".UPDE")){//欣扑
                        Intent mIntent =  new Intent(this,XPActivity.class);
                        mIntent.putExtra("PATH", path);
                        startActivity(mIntent);
                        finish();
                        return;
                    }else if(path.contains(".iap")){//威驰
                        Intent mIntent =  new Intent(this,WCActivity.class);
                        mIntent.putExtra("PATH", path);
                        startActivity(mIntent);
                        finish();
                        return;
                    }else if(path.contains(".bin")&& path.contains("FJT")){//FJT
                        Intent mIntent =  new Intent(this,JFTActivity.class);
                        mIntent.putExtra("PATH", path);
                        startActivity(mIntent);
                        finish();
                        return;
                    }
                }
            }else{
                Toast.makeText(CanUpdateMainActivity.this, getResources().getString(R.string.no_upgrade_files), Toast.LENGTH_SHORT).show();
                return;
            }
        } catch (Exception e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }

不同的协议公司升级文件后缀不一样, path.contains(".bin"),有的升级文件由于担心后缀太常见容易混淆,我们甚至加上前缀判断 path.contains("FJT"),一切只为确保升级程序的指向精确,拿到相关路径后,进入不同的 Activity

2,进入RESET模式

不同的车型进入的界面不同,除增加我们公司一些自带的通讯框架之外,逻辑基本相同,先显示从上个界面收到的路径内容:

private void getUSBFilePath() {
        try {

            if(fis != null){
                fis.close();
                fis = null;
            }

            path = (String) getIntent().getExtra("PATH", "");
            if(path != null && !path.equals("")){

                Log.i(TAG, path);

                File f = new File(path);
                fileByteLength = f.length();
                Log.i(TAG, "升级文件路径:"+path+"\n 文件字节大小:"+fileByteLength);
                ((TextView)findViewById(R.id.tv_file_path)).setText(getResources().getString(R.string.update_file_path)+path);

            }
        } catch (Exception e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

这样做是方便顾客确认升级文件等信息,有的协议公司需要在指令之前发送一切其他的暂停之类的指令,看情况而定,然后发送RESET指令: mTWCS.ssWrite3(STATUS_OVER,0x00,0x00);不同的协议公司,升级的指令不同,甚至发送的格式也不同,这样拿威驰的做示例 STATUS_OVER是一个全局变量,用于更新当前升级步骤

3,校验一些数据密码等

我们通讯是通过MCU做媒介,我们将指令封装发给MCU,MCU解析后将对应的格式发给can,同理CAN回复的信息也走MCU通道,我们接受MCU的数据是通过handler消息进行通信,一般发送RESET指令后,机器会进入升级模式,我们就可以开始与can盒子开始通讯了

private Handler mHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            try {
                switch (msg.what) { 
                              }
            }catch (Exception e) {
                Log.e(TAG, Log.getStackTraceString(e));
            }
        }
    };

一般公司会先校验一下升级包的数据内容,

File f = new File(path);
                fileByteLength = f.length();
                Log.i(TAG, "升级文件路径:"+path+"\n 文件字节大小:"+fileByteLength);
                ((TextView)findViewById(R.id.tv_file_path)).setText(getResources().getString(R.string.update_file_path)+path);

                String fileLen = Long.toHexString(fileByteLength);
                if(fileLen.length()<=8){
                    //补位
                    int coverFrontLength = 8 - fileLen.length();
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < coverFrontLength; i++) {
                        sb.append("0");
                    }
                    sb.append(fileLen);
                    fileLen = sb.toString();
                    fileLen1 = Integer.parseInt(fileLen.substring(0, 2), 16);
                    fileLen2 = Integer.parseInt(fileLen.substring(2, 4), 16);
                    fileLen3 = Integer.parseInt(fileLen.substring(4, 6), 16);
                    fileLen4 = Integer.parseInt(fileLen.substring(6, 8), 16);
                    Log.i(TAG, "file1234:"+fileLen1+","+fileLen2+","+fileLen3+","+fileLen4);
                }
            }

然后将这些fillen数据发给盒子,有的则是发送一段密码

4,发送数据

protected void sendDate(){
        try {
            if(fis == null)
                fis = new FileInputStream(new File(path));
            byte[] bytes = new byte[1];

            int i = 0;
            int datasTemp = 0;
            if(fileByteLength > indexTotal*64){

                if(pd != null && nt != null){
                    double pro = (double)indexTotal*64/(double)fileByteLength;
                    nt.setMinimumFractionDigits(0);
                    String progress = nt.format(pro);
                    pd.setMessage(getResources().getString(R.string.upgrade_progress)+progress);
                }

                // 判断文件字节长度是否大于当前已读取的字节长度,如果大于就相减,
                // 判断结果值是否大于64或者是否等于64,如果小于,即:已读取到最后一包数据,这包数据要单独处理
                // Log.i(TAG, "indexTotal*64:"+indexTotal*64);
                if((fileByteLength - indexTotal*64) > 64){
                    byte[] datas64 = new byte[64];
                    while (i < 64) {
                        datasTemp = fis.read(bytes,0,bytes.length);

                        if(datasTemp == -1)
                            break;

                        datas64[i] = bytes[0];
                        Log.i("md","bytes[0]: "+(bytes[0]&0xff));
                        num = (bytes[0]&0xff) +datasTemp +num;
                        i++;
                    }

                    //上传数据
                    mTWCS.ssWrite(0xbc, datas64);
                    indexTotal++;
                }else if((fileByteLength - indexTotal*64) == 64){
                    byte[] datas64 = new byte[64];
                    while (i < 64) {
                        datasTemp = fis.read(bytes,0,bytes.length);

                        if(datasTemp == -1)
                            break;

                        datas64[i] = bytes[0];
                        num = (bytes[0]&0xff) +datasTemp +num;
                        i++;
                    }

                    Log.i(TAG, "发送最后一包数据");
                    //上传数据
                    mTWCS.ssWrite(0xcf, datas64);
                }else{
                    int endLenth = (int)(fileByteLength - indexTotal*64);
                    byte[] datasEnd = new byte[endLenth];
                    while (i < endLenth) {
                        datasTemp = fis.read(bytes,0,bytes.length);

                        if(datasTemp == -1)
                            break;

                        datasEnd[i] = bytes[0];
                        num = (bytes[0]&0xff) +datasTemp +num;
                        i++;
                    }

                    Log.i(TAG, "发送最后一包数据");
                    //上传数据
                    mTWCS.ssWrite(0xcf, datasEnd);
                }
                Log.i("md", "num  "+num);
            }

        } catch (Exception e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

这里,理解好这个方法就好,

while (i < 64) {
                        datasTemp = fis.read(bytes,0,bytes.length);
                        if(datasTemp == -1)
                            break;
                        datas64[i] = bytes[0];
                        num = (bytes[0]&0xff) +datasTemp +num;
                        i++;
                    }

64是我们发送的数据长度,datas64就是我们每次发给盒子的数据段,fis.read是读取file文件数据的方法

5,接受是否升级成功

一般来说,能发完数据内容,基本就没有问题,有的公司甚至都不会给你回升级成功与否的指令,把数据发送完成当作升级完成,有的公司是在发生完毕后给你发一段指令,指令告诉你升级成功还是失败,你适当显示对应的内容就行!

原文发布于微信公众号 - 项勇(xiangy_life)

原文发表时间:2018-08-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏猿湿Xoong

Android 8.0 SystemUI(四):二说顶部 StatusBar

大家好,我是ptt,本篇是 SystemUI 的第四篇,也是 StatusBar 的第二说。

8514
来自专栏Android知识点总结

3-AII--BroadcastReceiver实现锁、开屏、短信监听

1403
来自专栏杨建荣的学习笔记

Oracle 12c PDB中碰到的DG问题 (r10笔记第63天)

Oracle 12c中的PDB一下子让数据文件的格式复杂了一些,所以Data Guard就很有必要了,一旦出现问题,受损失的数据库是全局的。没想到在搭建Data...

35611
来自专栏Android开发实战

Android 组件化 —— 路由设计最佳实践

Android原生已经支持AndroidManifest去管理App跳转,为什么要有路由库,这可能是大部分人接触到Android各种Router库不太明白的地方...

1782
来自专栏Android研究院

Android组件化专题 - 路由框架进阶模块间的业务通信

上一篇文章,讲解了路由框架实现的原理,并实现了基本的路由框架 页面路由的跳转 Android组件化专题 - 路由框架原理。

1292
来自专栏做全栈攻城狮

安卓开发基础教程-Android多界面应用程序开发

本套教程主要讲解安卓开发的相关知识,从基础到精通。一方面可以巩固自己所得,另一方面可以帮助对安卓开发感兴趣的朋友。

1174
来自专栏Android先生

Android小技巧: 这里涵盖了所有实现 “一键退出 App” 的方法

即 需要2个步骤 才可 完成 一键退出 App 需求。下面,我将根据这两个步骤进行功能实现讲解。

722
来自专栏技术小黑屋

Android内存泄漏检测利器:LeakCanary

到这里你就可以检测到Activity的内容泄露了。其实现原理是设置Application的ActivityLifecycleCallbacks方法监控所有Act...

1282
来自专栏技术小黑屋

Android处理崩溃的一些实践

对于任何程序来说,崩溃都是一件很难避免的事情,当然Android程序也不例外。在Android程序中,引起崩溃的多属于运行时异常或者错误,对于这些异常我们很难做...

1222
来自专栏郭霖

Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能

我们的Glide系列文章终于要进入收尾篇了。从我开始写这个系列的第一篇文章时,我就知道这会是一个很长的系列,只是没有想到竟然会写这么久。 在前面的六篇文章中,我...

4745

扫码关注云+社区

领取腾讯云代金券