【嵌入式】基于串口的IAP在线升级详解与实战3----安卓APP移植Ymodem远程升级嵌入式固件

news/2024/6/17 5:35:50 标签: java, android, 安卓, 嵌入式

目录

安卓程序设计

二 创建文件选择器

安卓遇上Ymodem

四 一些坑和注意点汇总


安卓程序设计

        根据【嵌入式】基于串口的IAP在线升级详解与实战1----IAP功能设计中的说明,已经完成了MCU-HC08-超级终端的固件烧写流程,距离设想的使用手机APP实现MCU固件烧写只有一步之遥。剩下来的工作就是将1K-Ymodem协议移植到手机APP中,再结合手机蓝牙,将需要烧写的固件发送到MCU的IAP程序中。

        APP中要实现的功能大致分为以下几点:

        (1)设计一个文件选择器,可以加载手机本地文件(固件bin文件),并返回文件名和文件路径;

        (2)移植Ymodem协议,并嵌入蓝牙读写;

        (3)烧写进度显示以及一些其他的功能;

二 创建文件选择器

        此处参考android 调用系统文件管理器,可以通过调用手机自带的文件管理器访问文件,点击之后响应回调,返回文件名和文件路径。

安卓遇上Ymodem

        这边首先提供两篇参考资料:

  1. 当Android BLE遇上YModem
  2. 安卓相关】蓝牙基于Ymodem协议发送bin文件,对硬件设备进行升级

        还有两个相关的github开源库:

  1. LeonXtp / YModemForAndroid
  2. ArdWang / YModemlib_Android

        这两个库里面的实现基本上是差不多的,我这边使用的是第二个库,支持经典蓝牙socket通讯 和BLE。

【1】在项目中添加该库:

方法一:Gradle使用(在build.gradle中添加下列依赖,但是这样导入的库是只读的,不能修改):

    allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

    dependencies {
            implementation 'com.github.ArdWang:YModemLib:2.0.0'
    }

方法二:AndroidStudio中导入module

【2】实例化Ymodem类:

文件选择好之后,调用startYmodem以开启Ymodem传输:

java">private void  startYmodem(){
        yModem = new YModem.Builder()
                .with(this)
                .filePath(Path_new) //存放到手机的文件路径 stroge/0/.../xx.bin 这种路径
                .fileName(Name)
                .checkMd5("") //Md5可以写可以不写 看自己的通讯协议
                .sendSize(1024) //可以修改成你需要的大小
                .callback(new YModemListener() {
                    @Override
                    public void onDataReady(byte[] data) {
                        MainActivity.writeData(data);
                    }

                    @Override
                    public void onProgress(int currentSent, int total) {
                        //进度条处理
                    }

                    @Override
                    public void onSuccess() {
                        //成功的显示
                    }

                    @Override
                    public void onFailed(String reason) {

                    }
                }).build();
        yModem.start();
    }

        这边主要关注一下filePath、fileName、sendSize(我这边用的是1K Ymodem,所以该属性赋值1024)。

【3】蓝牙数据发送:

同样是startYmodem中,在onDataReady方法里加入当前使用的蓝牙发送数据接口,我这边是封装的writeData()接口,底层是蓝牙原生的数据发送接口:

java">charaWrite.setValue(data);
mBluetoothGatt.writeCharacteristic(charaWrite);
java">public static void writeData(byte[] data){
        BluetoothGattService service=mBluetoothGatt.getService(write_UUID_service);
        charaWrite=service.getCharacteristic(write_UUID_chara);

        if (data.length>20)
        {//数据大于个字节 分批次写入
            Log.e(TAG, "writeData: length="+data.length);
            int num=0;
            if (data.length%20!=0){
                num=data.length/20+1;
            }else{
                num=data.length/20;
            }
            for (int i=0;i<num;i++){
                if (i==num-1){
                    tempArr=new byte[data.length-i*20];
                    System.arraycopy(data,i*20,tempArr,0,data.length-i*20);
                }else{
                    tempArr=new byte[20];
                    System.arraycopy(data,i*20,tempArr,0,20);
                }
                charaWrite.setValue(tempArr);
                mBluetoothGatt.writeCharacteristic(charaWrite);
                Log.e("YMODEM", "[BT]tempArr.length="+tempArr.length);
                Log.e("YMODEM", "[BT]tempArr="+ Arrays.toString(tempArr));
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        else
        {
            charaWrite.setValue(data);
            mBluetoothGatt.writeCharacteristic(charaWrite);
        }
    }

【4】蓝牙数据接收:

onDataReceivedFromBLE方法用于接收数据,将其嵌入到蓝牙接收模块中,每当手机蓝牙模块收到外部返回的数据,则将该数据用于Ymodem协议的返回判断中:

java">//用于接受到你蓝牙设备给你反馈的蓝牙信息
    public static void onDataReceivedFromBLE(byte[] data) {
        yModem.onReceiveData(data);
    }
java">
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            Log.e(TAG,"onCharacteristicChanged()"+characteristic.getValue());
            data=characteristic.getValue();
            s = new String(data);
            Log.e(TAG,s);

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                        UpDate.onDataReceivedFromBLE(data);
                        Log.e("YMODEM", "[RCV]data="+ Arrays.toString(data));
                    }
                }
            });

        综上,便将Ymodem移植到了自己的安卓APP中,可以通过按钮进入文件管理器,选择到需要的固件bin文件之后,将该文件的名称和路径返回给Yomodem作为数据发送的依据,同时Ymodem调用蓝牙底层的收发逻辑实施数据交换。

        至此,手机APP可以完成与超级终端一样的功能,即文件选择与发送。具体的操作步骤可以参考超级终端那一块的内容。

四 一些坑和注意点汇总

        实际的研发过程中遇到了不少的坑,这边做一个统一的汇总说明:

【1】握手阶段--蓝牙发包最大每包20字节

        【问题定位】:蓝牙BLE发包的MTU是20字节是其劣根性,可以通过Android 修改ble蓝牙20字节限制中的方法修改其MTU:ble蓝牙BluetoothGattCallback:onCharacteristicChanged接收数据时,被限制只能接收20字节(实际为23字节,其中3字节为ATT占用),要突破20字节需要在BluetoothGattCallback:onConnectionStateChange连接成功时加入一下设置:

int mut = 512
bluetoothGatt.requestMtu(mut)

        但是修改了MTU之后,其可靠性得不到保证,故这边对蓝牙底层发包逻辑做一下上层的封装,超过20字节的包,分批次发送。新的发包函数writeData()前文有所提及,这边不再赘述。

【2】收发阶段--首包正确发完之后发送数据包,但是数据包一般正确发送几个之后,就会断掉:

        【问题定位】:这个问题的原因在于,我们选用的蓝牙模块存在劣根性,发送大量数据中间需要人为增加间隔,否则就会出现问题:

针对这个问题,在发送逻辑中加上延时20ms逻辑,即正常:


 

【3】挥手阶段--中间的数据包全部发完之后,板子应该回ACK,如下所示:

但是实际情况是,下板会在这一段时间内,多回个C。

        【问题定位】目前这个问题原因不明,采取的规避方法是在MCU中做规避,在发送中间数据包的过程中,不发C,测试OK(MCU程序参考【嵌入式】基于串口的IAP在线升级详解与实战2----移植Ymodem协议)。

        


http://www.niftyadmin.cn/n/1295283.html

相关文章

Nginx技术手册

2019独角兽企业重金招聘Python工程师标准>>> Nginx技术手册: --------------------------------手册------------------------------- Nginx安装与配置-李强强.pdf http://vdisk.weibo.com/s/muClF Nginx高端资料-李强强.pdf http://vdisk.weibo.com/s/mYxoG …

[细节决定B度]之二分搜索也不易啊

实事求是的说二分搜索是我学习算法的时候学的最好&#xff0c;理解的最透彻&#xff0c;能够当时就写出代码的的算法。事到如今&#xff0c;就如我可以分分钟写出hello world一样&#xff0c;我可以分分钟写出一个二分搜索算法&#xff0c;曾经几何时&#xff0c;这曾经是我在大…

【嵌入式】NXP/LPC的CRP功能的使用(代码加密/解密)

目录 一 J-Flash读取MCU内部程序 二 程序加密 三 程序解密 一 J-Flash读取MCU内部程序 通过J-Flash可以读取到MCU内部的程序&#xff0c;步骤如下&#xff1a; &#xff08;1&#xff09;进入J-Flash&#xff0c;选择指定的芯片之后&#xff0c;点击连接Target-Connect&…

【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录2----移植emWin

一 emWin移植准备 完成了前一文点亮LCD屏之后&#xff0c;考虑到将要设计较为复杂的界面GUI&#xff0c;光用一些基本的绘图、显示字符接口不能满足要求&#xff0c;所以琢磨着再移植一套emWin&#xff0c;用来辅助设计GUI&#xff08;由emWin的用户手册中可以看到&#xff0c;…

【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录1----点亮LCD屏

目录 一 SPI屏的接线 二 SPI屏驱动初始化 三 SPI屏点亮 四 附录 一 SPI屏的接线 SPI屏的特点在于接线简单&#xff0c;只需要四根SPI线以及几个GPIO口即可驱动工作&#xff0c;但是由于非并口的&#xff0c;所以当像素比比较大的时候&#xff0c;刷新速度可能会受到考验。我…

j2ee调用Oracle带数组参数和游标的存储过程方法

2019独角兽企业重金招聘Python工程师标准>>> 环境&#xff1a;Oracle 10g; jboss4.2.2;jdk1.6;hibernate 3.2 需求&#xff1a;有一个数据量比较大的表tableA,大概有几十万数据。里面存放用户手机号码&#xff0c;现在要求批量保存至少几百的的手机号码&#xff0c…

面试官:Java 是如何实现线程间通信的?

正常情况下&#xff0c;每个子线程完成各自的任务就可以结束了。不过有的时候&#xff0c;我们希望多个线程协同工作来完成某个任务&#xff0c;这时就涉及到了线程间通信了。 本文涉及到的知识点&#xff1a; thread.join(), object.wait(), object.notify(), CountdownLa…

【嵌入式总线】TIA Portal V15软件在Profibus上的操作使用1--组态

目录 一 设备列表 二 设备组态 三 更改从机设备地址 一 设备列表 主站CPU&#xff1a;西门子CPU 1212C DC/DC/DC 订货号6ES7 212-1AE40-0XB0 Profibus模块&#xff1a;西门子CM 1243-5 订货号6GK7 243-5DX30-0XE0 从站设备&#xff1a;总线板搭载VPC3芯片 设备名EASY4711 二…