(综合例程1) 隐藏+次数
一、概述
以下Lua脚本代码实现了对STM32F1xx单片机的整片擦除
+烧录
+烧录次数限制
+USB优盘隐藏
。
(1)可以通过电脑端串口调试助手下发HEX数据指令来实现:
- 解除USB优盘隐藏
- 指令:
A0 01 01 01 01
- 指令:
- 设置烧录次数现实
- 指令:
B0 12 34 56 xx xx xx xx
,例如设置1000次:B0 12 34 56 00 00 03 E8
- 指令:
(2)可以通过C2M Mini核心板上的BTN1或BTN2小乌龟按键来实现:
- USB优盘隐藏功能开启
提示
其他型号的国产32单片机也可以参考下面的Lua脚本代码来实现。
二、main.lua 脚本代码
--[[
该 Lua 脚本代码实现功能: 当检测到按键按下后,对 xxx 型号单片机的 flash 先执行整片擦除(chip erase)
,然后将 FW.bin 文件按单片机页(page)大小,逐页写入单片机 flash 中,最后将 bin 文件的 CRC32 值和芯片中已烧录
内容的 CRC32 值进行对比,二者一样则说明 bin 文件烧录成功。同时增加了对USB虚拟优盘供禁用功能和烧录次数限制功能。
]]--
------以下内容由Keil Pack 提供的对应单片机型号的 XXX.flm flash 编程算法文件通过 flm2lua.exe 工具软件一键自动生成------
--开发者如果需要烧录其他型号的arm cortex-m mcu,请自行用 flm2lua.exe 工具生成后覆盖下面的内容
FLASH_START_ADDR = 0x08000000 --目标芯片flash烧录起始地址,请检查是否正确!
FLASH_PAGE_SIZE = 1024 --目标芯片flash页(page)大小,请检查是否正确!
program_syscall = {
breakpoint = 0x20000001,
static_base = 0x20000148,
stack_pointer = 0x2000094C
}
flash_algo = {
init = 0x20000021,
uninit = 0x20000053,
erase_chip = 0x20000065,
erase_sector = 0x2000009F,
program_page = 0x200000DD,
program_buffer = 0x2000014C,
algo_start = 0x20000000,
program_buffer_size = 0x00000400
}
flash_code = {
0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA, 0x2A001E52, 0x4770D1F2,
0x4603B510, 0x4C442000, 0x48446020, 0x48446060, 0x46206060, 0xF01069C0, 0xD1080F04, 0x5055F245,
0x60204C40, 0x60602006, 0x70FFF640, 0x200060A0, 0x4601BD10, 0x69004838, 0x0080F040, 0x61104A36,
0x47702000, 0x69004834, 0x0004F040, 0x61084932, 0x69004608, 0x0040F040, 0xE0036108, 0x20AAF64A,
0x60084930, 0x68C0482C, 0x0F01F010, 0x482AD1F6, 0xF0206900, 0x49280004, 0x20006108, 0x46014770,
0x69004825, 0x0002F040, 0x61104A23, 0x61414610, 0xF0406900, 0x61100040, 0xF64AE003, 0x4A2120AA,
0x481D6010, 0xF01068C0, 0xD1F60F01, 0x6900481A, 0x0002F020, 0x61104A18, 0x47702000, 0x4603B510,
0xF0201C48, 0xE0220101, 0x69004813, 0x0001F040, 0x61204C11, 0x80188810, 0x480FBF00, 0xF01068C0,
0xD1FA0F01, 0x6900480C, 0x0001F020, 0x61204C0A, 0x68C04620, 0x0F14F010, 0x4620D006, 0xF04068C0,
0x60E00014, 0xBD102001, 0x1C921C9B, 0x29001E89, 0x2000D1DA, 0x0000E7F7, 0x40022000, 0x45670123,
0xCDEF89AB, 0x40003000, 0x00000000
}
------以上内容由Keil Pack 提供的对应单片机型号的 XXX.flm flash 编程算法文件通过 flm2lua.exe 工具软件一键自动生成------
------以下内容由Keil Pack 提供的对应单片机型号的 STM32Fxxx_OPT.FLM flash 编程算法文件通过 flm2lua.exe 工具软件一键自动生成------
--开发者如果需要擦除其他型号的arm cortex-m mcu的读保护opt区域,请自行用 flm2lua.exe 工具生成后填充下面的内容
--opt 擦除lua数据结构覆盖区(这里用不上,可不用管)
------以上内容由Keil Pack 提供的对应单片机型号的 STM32Fxxx_OPT.FLM flash 编程算法文件通过 flm2lua.exe 工具软件一键自动生成------
BIN_FILE_NAME = "FW.bin" --bin文件名称
CRC32_ENABLE = 1 --1:使能CRC32校验, 0:不使能
SWCLK, SWDIO, RESET = "D6", "D5", "D7" --烧录器SWD接口引脚
ButtonIO = "D3"
GreenLedIO = "D2"
RedLedIO = "D1"
PowerIO = "D4"
--10ms中断回调函数
function LIB_10msTimerCallback()
--喂狗
LIB_WatchDogFeed()
end
--查看剩余烧录次数
function PrintUsage()
local UsageCnt
local Total
local InfoStr
Total = LIB_FlashGetNumber("0")
UsageCnt = LIB_FlashGetNumber("1")
InfoStr = string.format("Total:%d, UsageCnt:%d", Total, UsageCnt)
print(InfoStr) --USB口输出信息
LIB_DelayMs(100)--延迟100ms,让Print打印完成
end
--整片擦除,烧录,校准
function SWD_DownLoadFlash()
local Re=1 --ok
local Err
print("Start download flash ==================>>>")
--加载由flm文件生成的flash算法
LIB_SwdLoadFlashAlgo(flash_algo, program_syscall, flash_code)
--SWD初始化
Re,Err = LIB_SwdTarget("INIT_DEBUG")
if Re == 0 then print(Err) return Re else print("INIT_DEBUG ok") end
--复位目标芯片并进入flash编程模式,加载flash算法到SRAM
Re,Err = LIB_SwdTarget("TARGET_INIT", FLASH_START_ADDR, 0, 0, 0)
if Re == 0 then print(Err) return Re else print("TARGET_INIT ok") end
--擦除整片flash
Re,Err = LIB_SwdTarget("TARGET_ERASECHIP", 0, 0, 0, 0)
if Re == 0 then print(Err) return Re else print("TARGET_ERASECHIP ok") end
--读bin文件并烧入目标芯片flash中。每次读FLASH_PAGE_SIZE字节(比如1024字节),数量不够的用0xff凑
local readflag = 1
local addr = 0
local content_cnt = 0 --统计烧入的bin字节数量
local page_cnt = 0 --统计实际烧入的page数量
--按目标芯片page大小读取bin文件,每读取一个page烧录一个page,直到读完bin文件为止
while readflag == 1 do
LIB_GpioToggle(GreenLedIO)--绿切换亮灭,实现闪烁
GC(1)--内存回收,防止溢出
read_number, read_content = LIB_FastRdTf(BIN_FILE_NAME, addr, FLASH_PAGE_SIZE)
if read_number > 0 then
content_cnt = content_cnt + read_number
page_cnt = page_cnt + 1
--擦除扇区(前面已经chip擦除了,这里不需要扇区擦除,也因为每个单片机的扇区结构大小不一,不适合循环规律擦除)
--Re,Err = LIB_SwdTarget("TARGET_ERASESECTOR", addr, 0, 0, 0)
--if Re == 0 then print(Err) return Re else print("TARGET_ERASESECTOR ok") end
--按FLASH_PAGE_SIZE的大小进行页写,但read_content中的元素个数是可以小于页大小的
Re,Err = LIB_SwdTarget("TARGET_WR_PAGE", addr, FLASH_PAGE_SIZE, read_content)
if Re == 0 then print(Err) return Re else print(string.format("WRITE PAGE 0x%08X ok", addr+FLASH_START_ADDR)) end
addr = addr + FLASH_PAGE_SIZE
end
--已读完
if read_number < FLASH_PAGE_SIZE then
readflag = 0
end
end
LIB_GpioWrite(GreenLedIO, 1)--绿灯灭
--打印flash烧录总结信息:
print(string.format("Download bin file(%d bytes), actually writed:%d bytes (%d pages), page_size=%d",content_cnt,page_cnt*FLASH_PAGE_SIZE,page_cnt,FLASH_PAGE_SIZE))
--如果是空文件,则报错
if addr <= 0 then
print("Bin file zero size!")
return 0
end
--到这里基本算是可以将烧录次数+1了,如果你想更严苛一点,那可以把代码再往前放
local UsageCnt
UsageCnt = LIB_FlashGetNumber("1")
--更新烧录次数
UsageCnt = UsageCnt + 1
LIB_FlashWriteNumber("1", UsageCnt)
PrintUsage()
--CRC32校验
if CRC32_ENABLE == 1 then
print("Start CRC32 check...")
--记录烧录文件BIN_FILE的CRC32值
LIB_SwdTarget("TARGET_CRC32_FINAL")
--"TARGET_RD_CRC32CHECK": 开始计算目标芯片中已烧录的内容CRC32,并和BIN_FILE的CRC32对比
r1,r2,r3,r4 = LIB_SwdTarget("TARGET_RD_CRC32CHECK")
--crc报错
if r1 == 0 then
print(string.format("CRC32 fail, crc_flash=0x%08X,crc_file=0x%08X",r3,r4))
return r1
else
print("CRC32 chek ok!")
end
end
--目标芯片执行flash算法uninit,结束烧录状态
Re,Err = LIB_SwdTarget("TARGET_UNINIT", 0, 0, 0, 0)
if Re == 0 then print(Err) return Re else print("TARGET_UNINIT ok") end
print("End download flash success! (^_^)")
return Re
end
--擦除类似STM32单片机的OPT区域(读保护),这个函数不是必须的
function SWD_EraseOpt()
local Re=1 --ok
local Err
print("Start erase opt ==================>>>")
--加载由flm文件生成的flash算法
LIB_SwdLoadFlashAlgo(flash_opt_algo, program_opt_syscall, flash_opt_code)
--SWD初始化
Re,Err = LIB_SwdTarget("INIT_DEBUG")
if Re == 0 then print(Err) return Re else print("INIT_DEBUG ok") end
--复位目标芯片并进入flash编程模式,加载flash算法到SRAM
Re,Err = LIB_SwdTarget("TARGET_INIT", 0, 0, 0, 0)
if Re == 0 then print(Err) return Re else print("TARGET_INIT ok") end
--擦除opt中的内容
Re,Err = LIB_SwdTarget("TARGET_ERASECHIP", 0, 0, 0, 0)
if Re == 0 then print(Err) return Re else print("TARGET_ERASECHIP ok") end
--目标芯片执行flash算法uninit
Re,Err = LIB_SwdTarget("TARGET_UNINIT", 0, 0, 0, 0)
if Re == 0 then print(Err) return Re else print("TARGET_UNINIT ok") end
print("End erase opt success! (^_^)")
return Re
end
--各种初始化代码
--LIB_SystemLogEnable() --使能系统日志,该功能平时不需要
--LIB_LuaTfLogEnable() --使能lua代码中print可以在tf卡上生成日志,该功能平时不需要
--LIB_LuaTfLogDisable()
LIB_LuaHeapExpand("20KB") --烧录时比较费空间,从底层申请扩展20KB空间
LIB_UsbConfig("CDC")--使能usb转串口print打印功能
LIB_ButtonConfig("BTN1","D10","L")-- 小板上的BTN1小乌龟按键初始化
LIB_ButtonConfig("BTN2","D11","L")-- 小板上的BTN2小乌龟按键初始化
LIB_ButtonConfig("BTN3",ButtonIO,"L")-- 大板上的烧录按键初始化
LIB_GpioOutputConfig("D0","STANDARD")--DNLD IO始化,目前暂时没用
LIB_GpioWrite("D0", 0)
LIB_GpioOutputConfig(PowerIO,"STANDARD")--3V3 Power Mos控制IO始化
LIB_GpioWrite(PowerIO, 1)--给目标芯片供电
LIB_GpioOutputConfig(GreenLedIO,"STANDARD")--绿灯初始化
LIB_GpioWrite(GreenLedIO, 0)--绿灯亮
LIB_GpioOutputConfig(RedLedIO,"STANDARD")--红灯初始化
LIB_GpioWrite(RedLedIO, 1)--红灯灭
--初始SWD离线烧录功能(基于ARM-CMSIS DAP), "D5","D6","D7"分别对应SWCLK, SWDIO, RESET引脚
LIB_ArmSwdConfig(SWCLK, SWDIO, RESET, 72000000, 4000000, CRC32_ENABLE)
--配置开始工作,溢出时间为10秒
LIB_WatchDogConfig("10S")
--使能10ms中断定时器
LIB_10msTimerConfig("ENABLE")
print("Start up~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
--开始大循环
while(GC(1) == true)
do
--查询USB CDC虚拟串口是否收到数据
recv_flag,r_tab = LIB_UsbCdcRecv()
if recv_flag == 1 then
--如果串口收到 A0 00 00 00 00 则禁止USB虚拟优盘功能
if r_tab[1] == 0xA0 and r_tab[2] == 0x00 and r_tab[3] == 0x00 and r_tab[4] == 0x00 and r_tab[5] == 0x00 then
LIB_UsbDiskDisable(0xAB2233) --0xAB2233是未来调用Enable函数的密码,一定不要错了,不然再也无法恢复USB虚拟优盘
print("USB disk is locked!")
--如果串口收到 A0 01 01 01 01 则恢复USB虚拟优盘功能,间接来看: A0 01 01 01 01 相当于恢复密码,一定不要忘记
elseif r_tab[1] == 0xA0 and r_tab[2] == 0x01 and r_tab[3] == 0x01 and r_tab[4] == 0x01 and r_tab[5] == 0x01 then
LIB_UsbDiskEnable(0xAB2233)
print("USB disk is unlocked!")
--如果串口收到 B0 12 34 56 XX XX XX XX ,重新设置新的烧录次数,并清空使用次数,间接来看: B0 12 34 56 相当于设置烧录次数密码,一定不要忘记
--例如 B0 12 34 56 00 00 03 E8 表示设置1000次烧录次数
elseif r_tab[1] == 0xB0 and r_tab[2] == 0x12 and r_tab[3] == 0x34 and r_tab[4] == 0x56 then
LIB_FlashWriteNumber("0", r_tab[5]*256*256*256 + r_tab[6]*256*256 + r_tab[7]*256 + r_tab[8])
LIB_FlashWriteNumber("1", 0)
PrintUsage()
end
end
key_value = LIB_ButtonQuery("BTN1")
--如果检测小板上的BTN1小乌龟按键短按动作,同样也可以禁止USB虚拟优盘功能
if key_value == 1 then
print("USB disk is locked!")
PrintUsage()
print("USB disk is locked!")
LIB_UsbDiskDisable(0xAB2233) --0xAB2233是未来调用Enable函数的密码,一定不要错了,不然再也无法恢复USB虚拟优盘
end
key_value = LIB_ButtonQuery("BTN2")
--如果检测小板上的BTN2小乌龟按键短按动作,同样也可以禁止USB虚拟优盘功能
if key_value == 1 then
print("USB disk is locked!")
PrintUsage()
LIB_UsbDiskDisable(0xAB2233) --0xAB2233是未来调用Enable函数的密码,一定不要错了,不然再也无法恢复USB虚拟优盘
end
key_value = LIB_ButtonQuery("BTN3")
--如果检测到烧录按键短按动作,则启动一次 flash 烧录
if key_value == 1 then
--检测烧录次数是否超限制
local UsageCnt
local Total
Total = LIB_FlashGetNumber("0")
UsageCnt = LIB_FlashGetNumber("1")
if UsageCnt >= Total then
LIB_GpioWrite(GreenLedIO, 1)--绿灯灭
while (GC(1) == true) --次数超限就无限闪红灯
do
LIB_GpioWrite(RedLedIO, 0)--红灯亮
LIB_DelayMs(500)
LIB_GpioWrite(RedLedIO, 1)--红灯灭
LIB_DelayMs(500)
PrintUsage()
end
end
--绿灯亮,红灯灭
LIB_GpioWrite(GreenLedIO, 0)
LIB_GpioWrite(RedLedIO, 1)
--如果需要擦除类似stm32这种单片机的opt读保护
OptEraseOk = 1
if flash_opt_algo~= nil then
if SWD_EraseOpt() ~= 1 then --opt擦除失败
OptEraseOk = 0
print("Erase opt fail! (T_T)")
end
end
--开始烧录
if SWD_DownLoadFlash() == 1 and OptEraseOk== 1 then --成功
print("All finished successed! (^_^)")
LIB_GpioWrite(GreenLedIO, 0)--绿灯亮
LIB_GpioWrite(RedLedIO, 1)--红灯灭
else --失败
print("Download fail! (T_T)")
LIB_GpioWrite(GreenLedIO, 1)--绿灯灭
LIB_GpioWrite(RedLedIO, 0)--红灯亮
end
LIB_SwdTarget("SWD_OFF")--结束,让烧录器的swd接口呈高阻态
LIB_GpioWrite(PowerIO, 0)--目标芯片断电重启
LIB_DelayMs(100)--延时100ms
LIB_GpioWrite(PowerIO, 1)--目标芯片上电
end
end