通过Lua小程序,我们可以方便方便的在线方式做串口固件烧录,也可以离线方式运行Lua小程序做烧录。
本次是说明是采用H7-TOOL串口方式连接我们V7板子做的操作说明。 【协议说明】
1、发送固件大小:符号‘*’ 来同步,然后发送固件大小,板子收到后,回复0x30表示擦除相应扇区大小成功,回复0x60表示擦除失败。
2、发送固件数据:符号‘$’ 来同步,然后发送固件数据,每次224字节大小,板子收到后,回复0x30表示数据编程成功,回复0x60表示擦除失败。如此反复,一直到发送完毕。
3、发送结束命令:符号‘#’ 表示传输结束,目标板可以加载到APP运行了。
【硬件接线】
H7-TOOL通过TTL串口接到V7板子的TTL串口上:
【准备工作】
当前上位机还没有做专门的串口脱机烧录一键下载界面,需要手动将Lua文件和app固件存到
1、H7-TOOL进入虚拟U盘
上电首界面长按S键 -> 系统设置 -> USB eMMC磁盘, 进入eMMC模拟U盘后,在如下路径新建文件夹串口脱机烧录
将如下两个文件存到新建的文件夹下
app.bin (50.03 KB)
uartfirmware.lua (3.04 KB)
2、将目标板程序下载到V7开发板
基于V7的串口脱机烧录目标板程序.7z (2 MB) 【在线方式操作说明】
H7-TOOL可以采用USB,以太网或者WiFi方式连接上位机。
将前面lua小程序uartfirmware.lua的内容复制到如下窗口:
点击下面的执行按钮就可以看到动图更新了:
【离线方式操作说明】
操作TOOL显示屏,进入Lua小程序界面:
执行uartfirmware.lua小程序。
执行效果如下:
【Lua小程序简单说明】
注释非常详细:
代码语言:javascript复制-------------------------------------------------------
--
-- 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
-------------------------------------------------------
【目标板程序简单说明】
最关键的就是串口程序处理:
代码语言:javascript复制/*
*********************************************************************************************************
* 函 数 名: 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