硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4002|回复: 15
收起左侧

[脱机烧录] H7-TOOL串口脱机烧录操作说明,支持TTL串口,RS232和RS485(2022-06-30)

  [复制链接]

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2022-6-30 01:47:58 | 显示全部楼层 |阅读模式
通过Lua小程序,我们可以方便方便的在线方式做串口固件烧录,也可以离线方式运行Lua小程序做烧录。

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

【协议说明】

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

【硬件接线】
H7-TOOL通过TTL串口接到V7板子的TTL串口上:

image.png

【准备工作】

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

1、H7-TOOL进入虚拟U盘

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

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

app.bin (50.03 KB)
uartfirmware.lua (3.04 KB)

image.png

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

基于V7的串口脱机烧录目标板程序.7z (2 MB)

【在线方式操作说明】

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

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

image.png

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



【离线方式操作说明】

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

执行uartfirmware.lua小程序。

执行效果如下:

image.png

【Lua小程序简单说明】
注释非常详细:
[C] 纯文本查看 复制代码
-------------------------------------------------------
--   
--     H7-TOOL 串口脱机烧录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/串口脱机烧录/app.bin" -- 表示串口脱机烧录文件夹下存的文件
local filepath1 = "0:/H7-TOOL/Lua/串口脱机烧录"        -- 浏览串口脱机烧录文件下存的文件


-------------------------------------------------------
--   第1步:浏览串口脱机烧录文件夹下存的文件
-------------------------------------------------------
f_dir(filepath1)
print()

-------------------------------------------------------
--   第2步:设置串口波特率115200,奇偶校验位无,数据位8,停止位1
-------------------------------------------------------
uart_cfg(1, 115200, 0, 8, 1)

-------------------------------------------------------
--   第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)

-- 发送固件大小给目标板
uart_send(1, "*")    --发送*号表示固件大小命令
uart_send(1, string.char(byte0))  --发送固件大小
uart_send(1, string.char(byte1))
uart_send(1, string.char(byte2))
uart_send(1, string.char(byte3))
print("执行扇区擦除.....")

-- 等待返回数据,返回0x30表示擦除成功
len, str = uart_recive(1, 1, 10000)
if(str == '\x30') then 
  print("扇区擦除执行完毕")
else
  print("扇区擦除执行失败")
end

-------------------------------------------------------
--   第4步:发送固件大小
-------------------------------------------------------
offset = 0
-- 第1个参数是路径,第2个参数的偏移地址,第3个参数读取大小
-- 返回值bytes表示读取的字节数,bin表示都回的数据
bytes, bin = f_read(filepath, 0, 224) 
offset = offset + bytes

-- 读取数据为0,表示传输完毕
while(bytes > 0)
do
    -- 发送$表示开始传输固件命令
    uart_send(1, "$")
    uart_send(1, string.char(bytes))

    -- 发送固件数据给目标板
    uart_send(1, bin)
    
    -- 等待目标板返回确认数据0x30
    len, str = uart_recive(1, 1, 20000)
    -- 返回0x30表示发送成功
    if(str == '\x30') then 
      bytes, bin = f_read(filepath, offset, 224)  -- 继续读取数据
      offset = offset + bytes
      if(bytes ~= 0) then                         -- 读取不为0,打印发送的总字节数
         print("发送固件:", offset)
      end
    else
        print("固件传输失败")
    end
    
end

-------------------------------------------------------
--   第5步:发送传输结束命令
-------------------------------------------------------
uart_send(1, "#")
print("固件传输完成")

-------------------------------------------------------
--  end of file
-------------------------------------------------------


【目标板程序简单说明】
最关键的就是串口程序处理:

[C] 纯文本查看 复制代码
/*
*********************************************************************************************************
*        函 数 名: DemoUartUpdate
*        功能说明: 串口烧录
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
void DemoUartUpdate(void)
{
        uint8_t cmd;
        uint8_t ucStatus = 0;  /* 状态机标志 */
        uint32_t SectorCount = 0;
        uint32_t SectorRemain = 0;
        uint32_t i;
    uint32_t TotalSize = 0;
        uint8_t ucState;
        
        
        bsp_StartAutoTimer(0, 500);        /* 启动1个500ms的自动重装的定时器 */
        
        while (1)
        {
                /* 判断定时器超时时间 */
                if (bsp_CheckTimer(0))        
                {            
                        /* 每隔500ms 进来一次 */  
                        bsp_LedToggle(2);
                }

                if (comGetChar(COM1, &cmd))        /* 从串口读入一个字符(非阻塞方式) */
                {
                        switch (ucStatus)
                        {
                                case 0:
                                        /* 开始传输固件命令 **************/
                                        if(cmd == '$')
                                        {
                                                RecCplt = 0;
                                                ucStatus = 1;        
                                        }
                                        
                                        /* 传输完成命令 **************/
                                        if(cmd == '#')
                                        {
                                                RecCount = 0;
                                                RecCplt = 1;
                                                JumpToApp();
                                        }
                                        
                                        /* 接收固件大小命令 */
                                        if(cmd == '*')
                                        {
                                                ucStatus = 3; 
                                        }
                                        break;
                                        
                                /* 设置每帧传输字节数,默认设置的224字节 */
                                case 1:
                                        RecSize = cmd;
                                        ucStatus = 2; 
                                        break;
                                           
                                /* 将接收到的数据编程到内部Flash */
                                case 2:
                                        buf[RecCount0] = cmd;
                                       
                                        /* 接收够224个数据 */
                                        if(RecCount0 == (RecSize - 1))
                                        {
                                                ucStatus = 0;
                                                RecCount0 = 0;
                                                
                                                /* 编程内部Flash, */
                                                ucState = bsp_WriteCpuFlash((uint32_t)(AppAddr + TotalSize),  (uint8_t *)buf, RecSize);
                                                TotalSize += RecSize;
                                                
                                                /* 如果返回非0,表示编程失败 */
                                                if(ucState != 0)
                                                {
                                                        /* 返回0x60,表示编程失败 */
                                                        comSendChar(COM1, 0x60);
                                                }
                                                
                                                /* 返回0x30,表示编程成功 */
                                                comSendChar(COM1, 0x30);
                                        }
                                        else
                                        {
                                                RecCount++;
                                                RecCount0++;
                                        }
                                        break;
                                        
                                /* 根据接收到的文件大小,擦除相应大小的扇区 */
                                case 3:
                                        buf[RecCount0] = cmd;
                                       
                                        if(RecCount0 == 3)
                                        {
                                                ucStatus = 0;
                                                RecCount0 = 0;
                                                filesize = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
                                                uwAppSize = filesize;
                                                SectorCount = filesize/(128*1024);
                                                SectorRemain = filesize%(128*1024);        
                                                
                                                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,表示擦除成功 */
                                                comSendChar(COM1, 0x30);
                                        }
                                        else
                                        {
                                                RecCount0++;
                                        }
                                        break;
                        }
                }
        }
}



【参考资料】

之前更新过三期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




评分

参与人数 2金币 +40 收起 理由
byccc + 20 赞一个!
hqgboy + 20 很给力!

查看全部评分

回复

使用道具 举报

23

主题

1403

回帖

1472

积分

至尊会员

积分
1472
发表于 2022-6-30 11:48:36 | 显示全部楼层
坐等发布,怒赞
代码不规范,亲人两行泪!
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2022-7-1 02:36:17 | 显示全部楼层
终于更新完毕了。
回复

使用道具 举报

36

主题

2039

回帖

2147

积分

至尊会员

积分
2147
发表于 2022-7-2 10:21:25 | 显示全部楼层
今天正好闲来无事,打算玩下。
Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
回复

使用道具 举报

82

主题

401

回帖

667

积分

金牌会员

积分
667
QQ
发表于 2022-7-3 15:39:38 | 显示全部楼层
建议把  Ymode 协议也移植一下,可以收  也可以发 ,单片机采集的各种类型的数据文件就可以通过Ymode协议上传到电脑了 。电脑也可以传一些其他文件到单片机系统里面。
武汉天纵鹏元科技有限公司。承接嵌入式项目开发,相关技术交流。STM32,物联网,工业控制方向。QQ  408137104
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2022-7-3 16:01:11 | 显示全部楼层
雷鹏 发表于 2022-7-3 15:39
建议把  Ymode 协议也移植一下,可以收  也可以发 ,单片机采集的各种类型的数据文件就可以通过Ymode协议上 ...

楼主位的其实更简单。搞个Xmodem,Ymodem,Zmodem之类的,用户还得移植个modem协议,这点不方便。

不过TOOL串口助手可以搞个moden协议玩。方便用modem协议的人。
回复

使用道具 举报

1

主题

131

回帖

134

积分

初级会员

积分
134
发表于 2022-7-3 16:26:53 | 显示全部楼层
白哥,有时间可以搞个 xmodem 协议的啊,个人开发用楼主位固然是方便,但是有时项目开发,用什么还是得看客户的需求。比如做小米项目,用小米的WiFi模块,就必须要用 xmodem或ymodem 方式来实现 OTA.
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2022-7-3 22:42:03 | 显示全部楼层
www2205290064 发表于 2022-7-3 16:26
白哥,有时间可以搞个 xmodem 协议的啊,个人开发用楼主位固然是方便,但是有时项目开发,用什么还是得看客 ...

协议可以支持的,串口助手增加一个。

但是像小米wifi这种的,TOOL参与进来干什么呢,基本用不上。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2022-7-4 00:03:26 | 显示全部楼层
准备支持下X,Y,Zmodem,同时开发Lua接口实现。方便这种玩法的用户使用。
回复

使用道具 举报

1

主题

131

回帖

134

积分

初级会员

积分
134
发表于 2022-7-4 05:41:38 | 显示全部楼层
eric2013 发表于 2022-7-3 22:42
协议可以支持的,串口助手增加一个。

但是像小米wifi这种的,TOOL参与进来干什么呢,基本用不上。

主要是做测试工具,验证 OTA
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2022-7-4 09:06:43 | 显示全部楼层
www2205290064 发表于 2022-7-4 05:41
主要是做测试工具,验证 OTA

这种场景参与不进来,实用性太低,所以给大家分享个视频教程更实用。

BSP驱动视频教程开始串口专题,包含modbus和XYZmodem协议专题一起录制了(2022-07-04)
https://www.armbbs.cn/forum.php? ... 3583&fromuid=58
(出处: 硬汉嵌入式论坛)


回复

使用道具 举报

1

主题

131

回帖

134

积分

初级会员

积分
134
发表于 2022-7-4 09:48:58 | 显示全部楼层
eric2013 发表于 2022-7-4 09:06
这种场景参与不进来,实用性太低,所以给大家分享个视频教程更实用。

BSP驱动视频教程开始串口专题, ...

明白,期待串口专题
回复

使用道具 举报

0

主题

34

回帖

34

积分

新手上路

积分
34
发表于 2022-7-5 10:48:08 | 显示全部楼层
点赞支持下,功能越来越多了,会不会让工程师太依赖工具了
回复

使用道具 举报

23

主题

1403

回帖

1472

积分

至尊会员

积分
1472
发表于 2022-7-5 14:22:33 | 显示全部楼层
这个功能已经用来了,
代码不规范,亲人两行泪!
回复

使用道具 举报

2

主题

13

回帖

19

积分

新手上路

积分
19
发表于 2023-7-31 19:32:25 | 显示全部楼层
老师,我这个函数怎么未定义呢?也安装了lua的插件。。
9DFAFE0A-6D0E-47ad-810B-C9466D9F3FD0.png
6716CA60-EC02-4686-AF2B-6A0572175120.png
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2023-8-1 08:30:03 | 显示全部楼层
Le0122 发表于 2023-7-31 19:32
老师,我这个函数怎么未定义呢?也安装了lua的插件。。

VSCode的LUA插件不行,我们TOOL这个,大部分都是基于我们TOOL硬件注册的API
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-28 20:33 , Processed in 0.222269 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表