• H7TOOL的I2C接口方式脱机烧录操作方法,已经发布(20220716)


    通过Lua小程序,我们可以方便方便的在线方式做I2C接口方式固件烧录,也可以离线方式运行Lua小程序做烧录。

    本次是说明是采用H7-TOOL的I2C接口连接我们V7板子做的操作说明。

    【协议说明】

    1、发送固件大小:符号‘*’ 来同步,然后发送固件大小,板子收到后,回复0x30表示擦除相应扇区大小成功,回复0x60表示擦除失败。
    2、发送固件数据:符号‘$’ 来同步,然后发送固件数据,每次64字节大小,板子收到后,回复0x30表示数据编程成功,回复0x60表示擦除失败。如此反复,一直到发送完毕。
    3、发送结束命令:符号‘#’ 表示传输结束,目标板可以加载到APP运行了。

    要更新APP固件的I2C设备地址,我们设置为0x20,通信速度设置的100KHz。

    【硬件接线】

    H7-TOOL通过I2C接到V7板子的I2C接口上



    【准备工作】

    当前上位机还没有做专门的I2C接口脱机烧录一键下载界面,需要手动将Lua文件和app固件存到TOOL的eMMC

    1、H7-TOOL进入虚拟U盘

    上电首界面长按S键 -> 系统设置 -> USB eMMC磁盘, 进入eMMC模拟U盘后,在如下路径新建文件夹串口脱机烧录

    将如下两个文件存到新建的文件夹下

      app.bin (50.03 KB)
      i2cbootloader.lua (5.97 KB)



    2、将目标板程序下载到V7开发板

    硬件I2C从机实现。
      基于V7的I2C接口脱机烧录目标板程序.7z (5.39 MB)

    【在线方式操作说明】

    H7-TOOL可以采用USB,以太网或者WiFi方式连接上位机。

    将前面lua小程序i2cbootloader.lua的内容复制到如下窗口:

    点击下面的执行按钮就可以看到动图更新了:


    【离线方式操作说明】

    操作TOOL显示屏,进入Lua小程序界面:

    执行uartfirmware.lua小程序。

    执行效果如下:



    【Lua小程序简单说明】

    注释非常详细:

    -------------------------------------------------------
    --   
    --     H7-TOOL 的I2C脱机烧录Lua小程序实现
    --
    -------------------------------------------------------        
    local str
    local len
    local bytes
    local bin
    local offset
    local value
    local count
    local filesize
    local byte0
    local byte1
    local byte2
    local byte3
    local filepath = "0:/H7-TOOL/Lua/I2C脱机烧录/app.bin" -- 表示I2C脱机烧录文件夹下存的文件
    local filepath1 = "0:/H7-TOOL/Lua/I2C脱机烧录"        -- 浏览I2C脱机烧录文件下存的文件
    local ack
    local i, m
    local res
    local str_offset
    local str_offset1
    local str_offset2
     
    -------------------------------------------------------
    --   I2C设备地址,更新需要修改
    -------------------------------------------------------
    local _usAddress = 0x20 -- I2C设备地址
     
    -------------------------------------------------------
    --   第1步:浏览串口脱机烧录文件夹下存的文件
    -------------------------------------------------------
    f_dir(filepath1)
    print()
     
    -------------------------------------------------------
    --   第3步:发送固件大小,方便目标板擦除相应大小扇区
    -------------------------------------------------------
    -- 获取固件大小
    filesize=f_size(filepath)    
    print("============================================")
    str= string.format("固件大小:%d",filesize)
    print(str)
     
    -- 将固件大小转换成四个字节
    byte0 = ((filesize >> 0) & 0xFF)
    byte1 = ((filesize >> 8) & 0xFF)
    byte2 = ((filesize >> 16) & 0xFF)
    byte3 = ((filesize >> 24) & 0xFF)
     
    -- 设置I2C速度是100KHz
    i2c_bus("init", 100000)
     
    i2c_bus("start")   -- 启动
    ack = i2c_bus("send", _usAddress) -- 写操作
     
    if (ack ~= 0) then
        print("I2C从机无应答 send address")
        goto cmd_fail        -- 无应答
    end
     
    --发送固件大小给目标板
    --发送*号表示固件大小命令
    --发送固件大小
    --固定发送64字节,前5个字节是其它用途
    str_offset = string.format("%02d", 69 - 5)
    str= string.format("%c%c%c%c%c".."%"..str_offset.."s", 42, byte0, byte1, byte2, byte3, "A")
    print(str)
     
    ack = i2c_bus("send", str)  -- 发送数据
     
    if (ack ~= 0) then
        print("I2C从机无应答 send data")
        goto cmd_fail        -- 无应答
    end
     
    i2c_bus("stop")
     
     -------查询,直到设备执行完毕擦除---------
     for m=1, 50, 1 do
        i2c_bus("start")
        ack = i2c_bus("send", _usAddress) -- 读操作
     
        if (ack ~= 0) then
            print("I2C从机无应答,支持扇区擦除中")
        else
            break
        end
        delayms(300)
    end
     
     -------获取返回值-----------------------
    i2c_bus("start")
    ack = i2c_bus("send", _usAddress+1) -- 读操作
     
    str = i2c_bus("recive", 1) -- 读取17个字节数据
     
    if(str == '\x30') then 
        print("扇区擦除执行完毕")
    else
        print("扇区擦除执行失败")
    end
    i2c_bus("stop")
     
    -------------------------------------------------------
    --   第4步:发送固件大小
    -------------------------------------------------------
    offset = 0
    -- 第1个参数是路径,第2个参数的偏移地址,第3个参数读取大小
    -- 返回值bytes表示读取的字节数,bin表示都回的数据
    bytes, bin = f_read(filepath, 0, 64) 
    offset = offset + bytes
     
    -- 读取数据为0,表示传输完毕
    while(bytes > 0)
    do
     
        -- 发送$表示开始传输固件命令
        -- 发送固件数据给目标板
        -- 固定每次发送64个字节,前5个字节其它用途
        count = 69 - 2 - bytes
        str_offset = string.format("%02d", count)
        str_offset1 = string.format("%"..str_offset.."s", "A")
        str_offset2 =  string.format("$%c", bytes)
        str= str_offset2..bin..str_offset1
     
        i2c_bus("start")   -- 启动
        ack = i2c_bus("send", _usAddress) -- 写操作
     
        if (ack ~= 0) then
            print("I2C从机无应答 send address")
            goto cmd_fail        -- 无应答
        end
     
        ack = i2c_bus("send", str)  -- 发送数据
     
        if (ack ~= 0) then
            print("I2C从机无应答 send data")
            goto cmd_fail        -- 无应答
        end
     
        i2c_bus("stop")
         
        -------查询,直到设备执行完毕扇区编程---------
        for m=1, 50, 1 do
            i2c_bus("start")
            ack = i2c_bus("send", _usAddress) -- 写操作
     
            --ack = i2c_bus("check", _usAddress)
         
            if (ack ~= 0) then
                --print("I2C从机无应答 send address")
            else
                break
            end
            delayms(1)
        end
     
         -------获取返回值-----------------------
        i2c_bus("start")
        ack = i2c_bus("send", _usAddress+1) -- 读操作
        str = i2c_bus("recive", 1) -- 读取1个字节数据
        i2c_bus("stop")
        if(str == '\x30') then  -- 如果返回值是0x30,继续读取
            bytes, bin = f_read(filepath, offset, 64)  -- 继续读取数据
            offset = offset + bytes
            if(bytes ~= 0) then                       -- 读取不为0,打印发送的总字节数
                print("发送固件:", offset)
            end
        else
            print("扇区编程执行失败")  
        end
    end
     
    -------------------------------------------------------
    --   第5步:发送传输结束命令
    -------------------------------------------------------
    str_offset = string.format("%02d", 69 - 1)
    str= string.format("#".."%"..str_offset.."s", "A")
     
    i2c_bus("start")   -- 启动
    ack = i2c_bus("send", _usAddress) -- 写操作
     
    if (ack ~= 0) then
        print("I2C从机无应答 send address")
        goto cmd_fail        -- 无应答
    end
     
    ack = i2c_bus("send", str)  -- 发送数据
     
    if (ack ~= 0) then
        print("I2C从机无应答 send data")
        goto cmd_fail        -- 无应答
    end
     
    i2c_bus("stop")
     
    print("固件传输完成")
     
    ::cmd_fail::
        --  命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备
        --  发送I2C总线停止信号
        i2c_bus("stop")
     
    -------------------------------------------------------
    --  end of file
    -------------------------------------------------------

    【目标板程序简单说明】

    最关键的就是I2C程序处理:

    /*
    *********************************************************************************************************
    *        函 数 名: main
    *        功能说明: c程序入口
    *        形    参: 无
    *        返 回 值: 错误代码(无需处理)
    *********************************************************************************************************
    */
    int main(void)
    {
            uint32_t SectorCount = 0;
            uint32_t SectorRemain = 0;
            uint32_t i;
        uint32_t TotalSize = 0;
            uint8_t ucState;
             
     
            bsp_Init();                /* 硬件初始化 */
            PrintfLogo();        /* 打印例程信息到串口1 */
            PrintfHelp();        /* 打印操作提示信息 */
     
            bsp_StartAutoTimer(0, 100);        /* 启动1个100ms的自动重装的定时器 */
             
            /* 首次使用,先设置64字节接收 */
            g_i2cLen = 69;
            bsp_i2cReceive();
             
            /* 进入主程序循环体 */
            while (1)
            {
                    bsp_Idle();                /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
     
                    /* 判断定时器超时时间 */
                    if (bsp_CheckTimer(0))        
                    {
                            /* 每隔100ms 进来一次 */ 
                            bsp_LedToggle(2);
                    }
                     
            if (wTransferState != TRANSFER_WAIT)
            {
                            /* 传输固件 */
                            if(g_i2cRxBuf[0] == '*')
                            {
                                    /* 获取文件大小 */
                                    filesize = g_i2cRxBuf[1] + (g_i2cRxBuf[2] << 8) + (g_i2cRxBuf[3] << 16) + (g_i2cRxBuf[4] << 24);
                                    uwAppSize = filesize;
                                    for(int i = 0; i < 69; i++)
                                    {
                                            printf("%x ", g_i2cRxBuf[i]);
                                    }
                                     
                                    /* 根据文件大小执行擦除 */
                                    SectorCount = filesize/(128*1024);
                                    SectorRemain = filesize%(128*1024);        
                                     
                                    printf("filesize = %d\r\n", filesize);
                                    for(i = 0; i < SectorCount; i++)
                                    {
                                            bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
                                    }
                                     
                                    if(SectorRemain)
                                    {
                                            bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
                                    }
                                     
                                    /* 返回0x30,表示擦除成功 */
                                    g_i2cLen = 1;
                                    g_i2cTxBuf[0] = 0x30;
                                    bsp_i2cTransfer();
                                     
                                    /* 继续执行下次接收 */
                                    g_i2cLen = 69;
                                    bsp_i2cReceive();
                            }
                             
                            /* 传输完成命令 **************/
                            if(g_i2cRxBuf[0]  == '#')
                            {
                                    JumpToApp();
                            }
             
                            /* 开始传输固件命令 **************/
                            if(g_i2cRxBuf[0]  == '$')
                            {                                           
                                    /* 接收数据个数 */
                                    RecSize = g_i2cRxBuf[1];
                                     
                                    /* 编程内部Flash, */
                                    ucState = bsp_WriteCpuFlash((uint32_t)(AppAddr + TotalSize),  (uint8_t *)&g_i2cRxBuf[2], RecSize);
                                    TotalSize += RecSize;
                                    printf("=====%d\r\n", TotalSize);
                                     
                                    /* 如果返回非0,表示编程失败 */
                                    if(ucState != 0)
                                    {
                                            /* 返回0x60,表示编程失败 */
                                            g_i2cLen = 1;
                                            g_i2cTxBuf[0] = 0x60;
                                            bsp_i2cTransfer();
                                    }
                                     
                                    /* 返回0x30,表示编程成功 */ 
                                    g_i2cLen = 1;
                                    g_i2cTxBuf[0] = 0x30;
                                    bsp_i2cTransfer();
                                     
                                    /* 继续执行下次接收 */
                                    g_i2cLen = 69;
                                    bsp_i2cReceive();
                            }
                             
                    }
            }
    }


    【参考资料】

    之前更新过三期BootLoader的视频教程,可以作为参考学习:

    单片机bootloader专题,启动,跳转配置和调试下载的各种用法
    https://www.armbbs.cn/forum.php?mod=viewthread&tid=112792

    基于NAND,eMMC,SD卡和U盘的BootLoader实战,带CRC完整性校验
    https://www.armbbs.cn/forum.php?mod=viewthread&tid=113053

    单片机BootLoader的AES加密实战,含上位机和下位机代码全开源
    https://www.armbbs.cn/forum.php?mod=viewthread&tid=113361

  • 相关阅读:
    (转)java反射机制及简单工厂模式
    (转)JAVA反射机制理解
    (转)前缀、中缀、后缀表达式
    (转)java提高篇(四)-----理解java的三大特性之多态
    (转)java for循环的执行顺序和几种常用写法
    (转)JAVA堆栈操作
    POI 实现合并单元格以及列自适应宽度
    前端缓存支持的文件格式及请求方式
    freemarker在xml文件中遍历list数据
    freemarker在线编辑
  • 原文地址:https://www.cnblogs.com/armfly/p/16508159.html
Copyright © 2020-2023  润新知