• H7-TOOL的I2C接口方式脱机烧录操作方法,已经发布(2022-07-16)


    通过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小程序简单说明】

    注释非常详细:

    1. -------------------------------------------------------
    2. --
    3. -- H7-TOOL 的I2C脱机烧录Lua小程序实现
    4. --
    5. -------------------------------------------------------
    6. local str
    7. local len
    8. local bytes
    9. local bin
    10. local offset
    11. local value
    12. local count
    13. local filesize
    14. local byte0
    15. local byte1
    16. local byte2
    17. local byte3
    18. local filepath = "0:/H7-TOOL/Lua/I2C脱机烧录/app.bin" -- 表示I2C脱机烧录文件夹下存的文件
    19. local filepath1 = "0:/H7-TOOL/Lua/I2C脱机烧录" -- 浏览I2C脱机烧录文件下存的文件
    20. local ack
    21. local i, m
    22. local res
    23. local str_offset
    24. local str_offset1
    25. local str_offset2
    26. -------------------------------------------------------
    27. -- I2C设备地址,更新需要修改
    28. -------------------------------------------------------
    29. local _usAddress = 0x20 -- I2C设备地址
    30. -------------------------------------------------------
    31. -- 第1步:浏览串口脱机烧录文件夹下存的文件
    32. -------------------------------------------------------
    33. f_dir(filepath1)
    34. print()
    35. -------------------------------------------------------
    36. -- 第3步:发送固件大小,方便目标板擦除相应大小扇区
    37. -------------------------------------------------------
    38. -- 获取固件大小
    39. filesize=f_size(filepath)
    40. print("============================================")
    41. str= string.format("固件大小:%d",filesize)
    42. print(str)
    43. -- 将固件大小转换成四个字节
    44. byte0 = ((filesize >> 0) & 0xFF)
    45. byte1 = ((filesize >> 8) & 0xFF)
    46. byte2 = ((filesize >> 16) & 0xFF)
    47. byte3 = ((filesize >> 24) & 0xFF)
    48. -- 设置I2C速度是100KHz
    49. i2c_bus("init", 100000)
    50. i2c_bus("start") -- 启动
    51. ack = i2c_bus("send", _usAddress) -- 写操作
    52. if (ack ~= 0) then
    53. print("I2C从机无应答 send address")
    54. goto cmd_fail -- 无应答
    55. end
    56. --发送固件大小给目标板
    57. --发送*号表示固件大小命令
    58. --发送固件大小
    59. --固定发送64字节,前5个字节是其它用途
    60. str_offset = string.format("%02d", 69 - 5)
    61. str= string.format("%c%c%c%c%c".."%"..str_offset.."s", 42, byte0, byte1, byte2, byte3, "A")
    62. print(str)
    63. ack = i2c_bus("send", str) -- 发送数据
    64. if (ack ~= 0) then
    65. print("I2C从机无应答 send data")
    66. goto cmd_fail -- 无应答
    67. end
    68. i2c_bus("stop")
    69. -------查询,直到设备执行完毕擦除---------
    70. for m=1, 50, 1 do
    71. i2c_bus("start")
    72. ack = i2c_bus("send", _usAddress) -- 读操作
    73. if (ack ~= 0) then
    74. print("I2C从机无应答,支持扇区擦除中")
    75. else
    76. break
    77. end
    78. delayms(300)
    79. end
    80. -------获取返回值-----------------------
    81. i2c_bus("start")
    82. ack = i2c_bus("send", _usAddress+1) -- 读操作
    83. str = i2c_bus("recive", 1) -- 读取17个字节数据
    84. if(str == '\x30') then
    85. print("扇区擦除执行完毕")
    86. else
    87. print("扇区擦除执行失败")
    88. end
    89. i2c_bus("stop")
    90. -------------------------------------------------------
    91. -- 第4步:发送固件大小
    92. -------------------------------------------------------
    93. offset = 0
    94. -- 第1个参数是路径,第2个参数的偏移地址,第3个参数读取大小
    95. -- 返回值bytes表示读取的字节数,bin表示都回的数据
    96. bytes, bin = f_read(filepath, 0, 64)
    97. offset = offset + bytes
    98. -- 读取数据为0,表示传输完毕
    99. while(bytes > 0)
    100. do
    101. -- 发送$表示开始传输固件命令
    102. -- 发送固件数据给目标板
    103. -- 固定每次发送64个字节,前5个字节其它用途
    104. count = 69 - 2 - bytes
    105. str_offset = string.format("%02d", count)
    106. str_offset1 = string.format("%"..str_offset.."s", "A")
    107. str_offset2 = string.format("$%c", bytes)
    108. str= str_offset2..bin..str_offset1
    109. i2c_bus("start") -- 启动
    110. ack = i2c_bus("send", _usAddress) -- 写操作
    111. if (ack ~= 0) then
    112. print("I2C从机无应答 send address")
    113. goto cmd_fail -- 无应答
    114. end
    115. ack = i2c_bus("send", str) -- 发送数据
    116. if (ack ~= 0) then
    117. print("I2C从机无应答 send data")
    118. goto cmd_fail -- 无应答
    119. end
    120. i2c_bus("stop")
    121. -------查询,直到设备执行完毕扇区编程---------
    122. for m=1, 50, 1 do
    123. i2c_bus("start")
    124. ack = i2c_bus("send", _usAddress) -- 写操作
    125. --ack = i2c_bus("check", _usAddress)
    126. if (ack ~= 0) then
    127. --print("I2C从机无应答 send address")
    128. else
    129. break
    130. end
    131. delayms(1)
    132. end
    133. -------获取返回值-----------------------
    134. i2c_bus("start")
    135. ack = i2c_bus("send", _usAddress+1) -- 读操作
    136. str = i2c_bus("recive", 1) -- 读取1个字节数据
    137. i2c_bus("stop")
    138. if(str == '\x30') then -- 如果返回值是0x30,继续读取
    139. bytes, bin = f_read(filepath, offset, 64) -- 继续读取数据
    140. offset = offset + bytes
    141. if(bytes ~= 0) then -- 读取不为0,打印发送的总字节数
    142. print("发送固件:", offset)
    143. end
    144. else
    145. print("扇区编程执行失败")
    146. end
    147. end
    148. -------------------------------------------------------
    149. -- 第5步:发送传输结束命令
    150. -------------------------------------------------------
    151. str_offset = string.format("%02d", 69 - 1)
    152. str= string.format("#".."%"..str_offset.."s", "A")
    153. i2c_bus("start") -- 启动
    154. ack = i2c_bus("send", _usAddress) -- 写操作
    155. if (ack ~= 0) then
    156. print("I2C从机无应答 send address")
    157. goto cmd_fail -- 无应答
    158. end
    159. ack = i2c_bus("send", str) -- 发送数据
    160. if (ack ~= 0) then
    161. print("I2C从机无应答 send data")
    162. goto cmd_fail -- 无应答
    163. end
    164. i2c_bus("stop")
    165. print("固件传输完成")
    166. ::cmd_fail::
    167. -- 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备
    168. -- 发送I2C总线停止信号
    169. i2c_bus("stop")
    170. -------------------------------------------------------
    171. -- end of file
    172. -------------------------------------------------------

    【目标板程序简单说明】

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

    1. /*
    2. *********************************************************************************************************
    3. * 函 数 名: main
    4. * 功能说明: c程序入口
    5. * 形 参: 无
    6. * 返 回 值: 错误代码(无需处理)
    7. *********************************************************************************************************
    8. */
    9. int main(void)
    10. {
    11. uint32_t SectorCount = 0;
    12. uint32_t SectorRemain = 0;
    13. uint32_t i;
    14. uint32_t TotalSize = 0;
    15. uint8_t ucState;
    16. bsp_Init(); /* 硬件初始化 */
    17. PrintfLogo(); /* 打印例程信息到串口1 */
    18. PrintfHelp(); /* 打印操作提示信息 */
    19. bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 */
    20. /* 首次使用,先设置64字节接收 */
    21. g_i2cLen = 69;
    22. bsp_i2cReceive();
    23. /* 进入主程序循环体 */
    24. while (1)
    25. {
    26. bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    27. /* 判断定时器超时时间 */
    28. if (bsp_CheckTimer(0))
    29. {
    30. /* 每隔100ms 进来一次 */
    31. bsp_LedToggle(2);
    32. }
    33. if (wTransferState != TRANSFER_WAIT)
    34. {
    35. /* 传输固件 */
    36. if(g_i2cRxBuf[0] == '*')
    37. {
    38. /* 获取文件大小 */
    39. filesize = g_i2cRxBuf[1] + (g_i2cRxBuf[2] << 8) + (g_i2cRxBuf[3] << 16) + (g_i2cRxBuf[4] << 24);
    40. uwAppSize = filesize;
    41. for(int i = 0; i < 69; i++)
    42. {
    43. printf("%x ", g_i2cRxBuf[i]);
    44. }
    45. /* 根据文件大小执行擦除 */
    46. SectorCount = filesize/(128*1024);
    47. SectorRemain = filesize%(128*1024);
    48. printf("filesize = %d\r\n", filesize);
    49. for(i = 0; i < SectorCount; i++)
    50. {
    51. bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
    52. }
    53. if(SectorRemain)
    54. {
    55. bsp_EraseCpuFlash((uint32_t)(AppAddr + i*128*1024));
    56. }
    57. /* 返回0x30,表示擦除成功 */
    58. g_i2cLen = 1;
    59. g_i2cTxBuf[0] = 0x30;
    60. bsp_i2cTransfer();
    61. /* 继续执行下次接收 */
    62. g_i2cLen = 69;
    63. bsp_i2cReceive();
    64. }
    65. /* 传输完成命令 **************/
    66. if(g_i2cRxBuf[0] == '#')
    67. {
    68. JumpToApp();
    69. }
    70. /* 开始传输固件命令 **************/
    71. if(g_i2cRxBuf[0] == '$')
    72. {
    73. /* 接收数据个数 */
    74. RecSize = g_i2cRxBuf[1];
    75. /* 编程内部Flash, */
    76. ucState = bsp_WriteCpuFlash((uint32_t)(AppAddr + TotalSize), (uint8_t *)&g_i2cRxBuf[2], RecSize);
    77. TotalSize += RecSize;
    78. printf("=====%d\r\n", TotalSize);
    79. /* 如果返回非0,表示编程失败 */
    80. if(ucState != 0)
    81. {
    82. /* 返回0x60,表示编程失败 */
    83. g_i2cLen = 1;
    84. g_i2cTxBuf[0] = 0x60;
    85. bsp_i2cTransfer();
    86. }
    87. /* 返回0x30,表示编程成功 */
    88. g_i2cLen = 1;
    89. g_i2cTxBuf[0] = 0x30;
    90. bsp_i2cTransfer();
    91. /* 继续执行下次接收 */
    92. g_i2cLen = 69;
    93. bsp_i2cReceive();
    94. }
    95. }
    96. }
    97. }


    【参考资料】

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

    单片机bootloader专题,启动,跳转配置和调试下载的各种用法
    BSP视频教程第17期:单片机bootloader专题,启动,跳转配置和调试下载的各种用法(2022-06-10) - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!

    基于NAND,eMMC,SD卡和U盘的BootLoader实战,带CRC完整性校验
    BSP视频教程第18期:基于NAND,eMMC,SD卡和U盘的BootLoader实战,带CRC完整性校验(2022-06-16) - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!

    单片机BootLoader的AES加密实战,含上位机和下位机代码全开源
    BSP视频教程第19期:单片机BootLoader的AES加密实战,含上位机和下位机代码全开源(2022-06-26) - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!

  • 相关阅读:
    【C++】类和对象中的static和const
    8.1_[Java 方法]-类的带参方法
    Newman+Jenkins实现接口自动化测试
    vivado简单仿真入门
    滴滴一面:线程池任务,如何设置优先级?
    pytorch代码实现之CoordConv卷积
    GE千兆以太网光口模式协商原理
    【运维笔记】swow源码编译安装
    【MySQL】索引
    拼多多API批量获取商品详情信息
  • 原文地址:https://blog.csdn.net/Simon223/article/details/125941038