Browse Source

update:fota兼容ec618

alienwalker 3 years ago
parent
commit
19463c226b

+ 38 - 0
components/common/c_common.h

@@ -39,6 +39,19 @@ typedef struct
 	char FilePath[100];//升级包在文件系统中的绝对路径,如果在flash中则随意填写
 }CoreUpgrade_HeadStruct;
 
+typedef struct
+{
+	uint32_t MaigcNum; //升级包标识,标识不对直接抛弃
+	uint32_t CRC32;		//后续字节的CRC32校验,所有CRC32规则与ZIP压缩一致
+	uint32_t Param1;	//升级参数,其中byte0升级类型,byte1升级包存放位置,byte2外设总线序号,和platform_define里的外设序号一致
+	uint32_t Param2;	//额外的参数,需要和外置存储总线配合使用,一般是外置存储总线的PIN
+	uint32_t DataStartAddress;//升级包在flash中的起始地址,外部和内部都可以用
+	uint32_t DataLen;//升级包大小
+	uint32_t MainVersion[5];//目标的底层版本,升级成功的话就是这个版本号了,有些SDK无法提供版本号,用MD5代替
+	uint8_t CommonMD5[16];//升级包整体数据的MD5
+	char FilePath[100];//升级包在文件系统中的绝对路径,如果在flash中则随意填写
+}CoreUpgrade_HeadCalMD5Struct;
+
 typedef struct
 {
 	uint32_t MaigcNum; //升级包标识,标识不对直接抛弃
@@ -50,6 +63,19 @@ typedef struct
 	uint32_t DataCRC32;//升级包整包数据的CRC32,这里验证传输的准确性
 }CoreUpgrade_FileHeadStruct;
 
+typedef struct
+{
+	uint32_t MaigcNum; //升级包标识,标识不对直接抛弃
+	uint32_t CRC32;		//后续字节的CRC32校验
+	uint32_t MainVersion[5];//目标的底层版本,升级成功的话就是这个版本号了,有些SDK无法提供版本号,用MD5代替
+	uint32_t AppVersion;//整包的版本号
+	uint32_t STDVersion[5];//允许升级的底层版本号,有些SDK无法提供版本号,用MD5代替
+	uint32_t CommonDataLen;	//通用升级包数据长度,内容是CoreUpgrade_SectorStruct或者CoreUpgrade_SectorCalMD5Struct
+	uint32_t SDKDataLen;	//特殊升级包数据长度,一般是SDK闭源的升级包
+	uint8_t CommonMD5[16];	//通用升级包数据的MD5,这里验证传输的准确性
+	uint8_t SDKMD5[16];	//特殊升级包数据的MD5,这里验证传输的准确性
+}CoreUpgrade_FileHeadCalMD5Struct;
+
 typedef struct
 {
 	uint32_t MaigcNum; //升级包标识,标识不对直接抛弃
@@ -61,6 +87,18 @@ typedef struct
 	uint32_t StartAddress;	//烧写的起始地址
 }CoreUpgrade_SectorStruct;
 
+typedef struct
+{
+	uint32_t MaigcNum; //升级包标识,标识不对直接抛弃
+	uint32_t TotalLen;	//解压前占据的空间
+	uint32_t DataLen;	//解压后占据的空间,如果和TotalLen一样,则表示未启用压缩,不需要解压,也没有压缩参数
+						//如果是0,则表示是差分升级
+						//其他表示是整包升级,数据包经过了lzma压缩
+	uint8_t MD5[16];	//解压后的数据的MD5,这里验证烧录的正确性
+	uint32_t BlockLen;	//压缩时分隔的大小,一般是64K,128K或者256K
+	uint32_t StartAddress;	//烧写的起始地址
+}CoreUpgrade_SectorCalMD5Struct;
+
 typedef struct
 {
 	uint32_t param_max_num;

+ 144 - 0
demo/soc_fota/EC618/libnet.lua

@@ -0,0 +1,144 @@
+local libnet = {}
+
+--- 阻塞等待网卡的网络连接上,只能用于任务函数中
+-- @string 任务标志
+-- @int 超时时间,如果==0或者空,则没有超时一致等待
+-- @... 其他参数和socket.linkup一致
+-- @return 失败或者超时返回false 成功返回true
+function libnet.waitLink(taskName, timeout, ...)
+	local is_err, result = socket.linkup(...)
+	if is_err then
+		return false
+	end
+	if not result then
+		result = sys_wait(taskName, socket.LINK, timeout)
+	else
+		return true
+	end
+	if type(result) == 'table' and result[2] == 0 then
+		return true
+	else
+		return false
+	end
+end
+
+--- 阻塞等待IP或者域名连接上,如果加密连接还要等握手完成,只能用于任务函数中
+-- @string 任务标志
+-- @int 超时时间,如果==0或者空,则没有超时一致等待
+-- @... 其他参数和socket.connect一致
+-- @return 失败或者超时返回false 成功返回true
+function libnet.connect(taskName,timeout, ... )
+	local is_err, result = socket.connect(...)
+	if is_err then
+		return false
+	end
+	if not result then
+		result = sys_wait(taskName, socket.ON_LINE, timeout)
+	else
+		return true
+	end
+	if type(result) == 'table' and result[2] == 0 then
+		return true
+	else
+		return false
+	end
+end
+
+--- 阻塞等待客户端连接上,只能用于任务函数中
+-- @string 任务标志
+-- @int 超时时间,如果==0或者空,则没有超时一致等待
+-- @... 其他参数和socket.listen一致
+-- @return 失败或者超时返回false 成功返回true
+function libnet.listen(taskName,timeout, ... )
+	local is_err, result = socket.listen(...)
+	if is_err then
+		return false
+	end
+	if not result then
+		result = sys_wait(taskName, socket.ON_LINE, timeout)
+	else
+		return true
+	end
+	if type(result) == 'table' and result[2] == 0 then
+		return true
+	else
+		return false
+	end
+end
+
+--- 阻塞等待数据发送完成,只能用于任务函数中
+-- @string 任务标志
+-- @int 超时时间,如果==0或者空,则没有超时一致等待
+-- @... 其他参数和socket.tx一致
+-- @return 
+-- @boolean 失败或者超时返回false,缓冲区满了或者成功返回true
+-- @boolean 缓存区是否满了
+function libnet.tx(taskName,timeout, ...)
+	local is_err, is_full, result = socket.tx(...)
+	if is_err then
+		return false, is_full
+	end
+	if is_full then
+		return true, true
+	end
+	if not result then
+		result = sys_wait(taskName, socket.TX_OK, timeout)
+	else
+		return true, is_full
+	end
+	if type(result) == 'table' and result[2] == 0 then
+		return true, false
+	else
+		return false, is_full
+	end
+end
+
+--- 阻塞等待新的网络事件或者特定事件,只能用于任务函数中
+-- @string 任务标志
+-- @int 超时时间,如果==0或者空,则没有超时一致等待
+-- @... 其他参数和socket.wait一致
+-- @return 
+-- @boolean 网络异常返回false,其他返回true
+-- @table or boolean 超时返回false,有新的数据到返回true,被其他事件退出的,返回接收到的事件
+function libnet.wait(taskName,timeout, netc)
+	local is_err, result = socket.wait(netc)
+	if is_err then
+		return false
+	end
+	if not result then
+		result = sys_wait(taskName, socket.EVENT, timeout)
+	else
+		return true
+	end
+	if type(result) == 'table' then
+		if result[2] == 0 then
+			return true, true
+		else
+			return false, false
+		end
+	else
+		return true, false
+	end
+end
+
+--- 阻塞等待网络断开连接,只能用于任务函数中
+-- @string 任务标志
+-- @int 超时时间,如果==0或者空,则没有超时一致等待
+-- @... 其他参数和socket.close一致
+-- @return 无
+function libnet.close(taskName,timeout, netc)
+	local is_err, result = socket.discon(netc)
+	if is_err then
+		socket.close(netc)
+		return
+	end
+	if not result then
+		result = sys_wait(taskName, socket.CLOSED, timeout)
+	else
+		socket.close(netc)
+		return
+	end
+	socket.close(netc)
+end
+
+return libnet

+ 35 - 0
demo/soc_fota/EC618/main.lua

@@ -0,0 +1,35 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "my_test"
+VERSION = "1.2"
+PRODUCT_KEY = "s1uUnY6KA06ifIjcutm5oNbG3MZf5aUv" --换成自己的
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+log.style(1)
+--require "gpio_test"
+-- require "i2c_test"
+-- require "audio_test"
+require "soc_fota"
+-- require "evb_lcd"
+-- require "timer_test"
+-- require "rotary"
+-- rotary_start()
+--require "fs_test"
+--fs_demo()
+--require "video_demo"
+-- require "sd"
+-- require "xmodem"
+-- pwm.open(2, 1000, 300,1000,1000)
+-- sys.taskInit(sendFile,3, 115200, "/sd/fw/air101.fls")
+-- require "camera_test"
+-- require "camera_raw"
+-- camDemo("10.0.0.3", 12000)
+-- require "core_lcd"
+-- require "lvgl_test"
+-- require "pm_test"
+-- require "no_block_test"
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 193 - 0
demo/soc_fota/EC618/soc_fota.lua

@@ -0,0 +1,193 @@
+-- 用http方式下载ota固件并实现升级
+--[[
+-- 这个是用uart3直接把升级包发给mcu,作为本地升级的一种参考,高速串口单次发送不要超过4K
+local rbuff = zbuff.create(16384)
+local function uartRx(id, len)
+    uart.rx(id, rbuff)
+    if rbuff:used() > 0 then 
+        local isError,fotaDone,nCache = fota.run(rbuff)
+        
+        if not isError then
+            
+        else
+            log.error("fota写入异常,请至少在1秒后重试")
+            fota.finish(false)
+            return
+        end
+        rbuff:del()
+        -- log.info("收到服务器数据,长度", rbuff:used(), "fota结果", isError, done, "总共", filelen)
+        if fotaDone then
+            log.info("下载完成")
+            sys.publish("downloadOK")
+        end
+    end
+end
+
+local function otaTask()
+    local spiFlash = spi.deviceSetup(spi.SPI_1,pin.PA7,0,0,8,24*1000*1000,spi.MSB,1,1)
+    fota.init(0, 0x00200000, spiFlash)
+    while not fota.wait() do
+        sys.wait(100)
+    end
+    
+    uart.setup(3,1500000)
+    uart.on(3, "receive", uartRx)
+    uart.write(3,"ready")
+    sys.waitUntil("downloadOK")                       
+    while true do
+        local isError,fotaDone  = fota.isDone()
+        if fotaDone then
+            fota.finish(true)
+            log.info("FOTA完成")
+            rtos.reboot()   --如果还有其他事情要做,就不要立刻reboot
+            break
+        end
+        sys.wait(100)
+    end
+end
+function otaDemo()
+    sys.taskInit(otaTask)
+end
+]]
+
+
+local libnet = require "libnet"
+
+local taskName = "OTA_TASK"
+local function netCB(msg)
+    log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
+end
+
+local function otaTask()
+    fota.init()
+    local isError, param, ip, port, total, findhead, filelen, rcvCache,d1,d2,statusCode,retry,rspHead,rcvChunked,done,fotaDone,nCache
+    local tbuff = zbuff.create(512)
+    local rbuff = zbuff.create(4096)
+    local netc = socket.create(nil, taskName)
+    socket.config(netc, nil, nil, nil) -- http用的普通TCP连接
+    filelen = 0
+    total = 0
+    retry = 0
+    done = false
+    rspHead = {}
+    local result = libnet.waitLink(taskName, 0, netc)
+    while retry < 3 and not done do
+        result = libnet.connect(taskName, 5000, netc, "www.air32.cn", 80) --后续出了http库则直接用http来处理
+        tbuff:del()
+        -- 用的iot平台,所以固件名称和版本号需要对应处理
+        -- 用的自建平台,可以自主定制规则
+        local v = rtos.version()
+        v = tonumber(v:sub(2, 5))
+        tbuff:copy(0, "GET /output.sota" .. " HTTP/1.1\r\n")
+        tbuff:copy(nil,"Host: www.air32.cn:80\r\n")
+        if filelen > 0 then --断网重连的话,只需要下载剩余的部分就行了
+            tbuff:copy(nil,"Range: bytes=" .. total .. "-\r\n") 
+        end
+        
+        tbuff:copy(nil,"Accept: application/octet-stream\r\n\r\n")
+        log.info(tbuff:query())
+        result = libnet.tx(taskName, 5000, netc, tbuff)
+        rbuff:del()
+        findhead = false
+        while result do
+            isError, param, ip, port = socket.rx(netc, rbuff)
+            if isError then
+                log.info("服务器断开了", isError, param, ip, port)
+                break
+            end
+            if rbuff:used() > 0 then
+                if findhead then
+                    isError,fotaDone,nCache = fota.run(rbuff)
+                    
+                    if not isError then
+                        total = total + rbuff:used()
+                    else
+                        log.error("fota写入异常,请至少在1秒后重试")
+                        fota.finish(false)
+                        done = true
+                        break
+                    end
+                    rbuff:del()
+                    -- log.info("收到服务器数据,长度", rbuff:used(), "fota结果", isError, done, "总共", filelen)
+                    if fotaDone then
+                        log.info("下载完成")
+                        while true do
+                            isError,fotaDone  = fota.isDone()
+                            if fotaDone then
+                                fota.finish(true)
+                                log.info("FOTA完成")
+                                done = true
+                                rtos.reboot()   --如果还有其他事情要做,就不要立刻reboot
+                                break
+                            end
+                            sys.wait(100)
+                        end
+                        break
+                    end
+                else
+                    rcvCache = rbuff:query()
+                    d1,d2 = rcvCache:find("\r\n\r\n")
+                    -- 打印出http response head
+                    -- log.info(rcvCache:sub(1, d2))    
+                    if d2 then
+                        --状态行
+                        _,d1,statusCode = rcvCache:find("%s(%d+)%s.-\r\n")
+                        if not statusCode then
+                            log.info("http没有状态返回")
+                            break
+                        end
+                        statusCode = tonumber(statusCode)
+                        if statusCode ~= 200 and statusCode ~= 206 then
+                            log.info("http应答不OK", statusCode)
+                            done = true
+                            break
+                        end
+                        --应答头
+                        for k,v in string.gmatch(rcvCache:sub(d1+1,d2-2),"(.-):%s*(.-)\r\n") do
+                            rspHead[k] = v
+                            if (string.upper(k)==string.upper("Transfer-Encoding")) and (string.upper(v)==string.upper("chunked")) then rcvChunked = true end
+                        end
+                        if filelen == 0 and not rcvChunked then 
+                            if not rcvChunked then
+                                filelen = tonumber(rspHead["Content-Length"] or "2147483647")
+                            end
+                        end
+                        --未处理的body数据
+                        rbuff:del(0, d2)
+                        isError,fotaDone,nCache = fota.run(rbuff)
+                        if not isError then
+                            total = total + rbuff:used()
+                        else
+                            log.error("fota写入异常,请至少在1秒后重试")
+                            fota.finish(false)
+                            done = true
+                            break
+                        end
+                        rbuff:del()
+                        -- log.info("收到服务器数据,长度", rbuff:used(), "fota结果", isError, done, "总共", filelen)
+                    else
+                        break
+                    end
+                    findhead = true
+                end
+            end 
+            result, param = libnet.wait(taskName, 5000, netc)
+            if not result then
+                log.info("服务器断开了", result, param)
+                break
+            elseif not param then
+                log.info("服务器没有数据", result, param)
+                break
+            end
+        end
+        libnet.close(taskName, 5000, netc)
+        retry = retry + 1
+    end
+    socket.release(netc)
+    sysplus.taskDel(taskName)
+    fota.finish(false)
+end
+
+function otaDemo()
+    sysplus.taskInitEx(otaTask, taskName, netCB)
+end

+ 3 - 2
luat/modules/luat_lib_fota.c

@@ -14,8 +14,8 @@
 #include "luat_log.h"
 
 /**
-初始化fota流程,目前只有105能使用
-@api fota.fotaInit(storge_location, param1)
+初始化fota流程
+@api fota.fotaInit(storge_location, len, param1)
 @int/string fota数据存储的起始位置<br>如果是int,则是由芯片平台具体判断<br>如果是string,则存储在文件系统中<br>如果为nil,则由底层决定存储位置
 @int 数据存储的最大空间
 @userdata param1,如果数据存储在spiflash时,为spi_device
@@ -23,6 +23,7 @@
 @usage
 -- 初始化fota流程
 local result = mcu.fotaInit(0, 0x00300000, spi_device)	--由于105的flash从0x01000000开始,所以0就是外部spiflash
+local result = mcu.fotaInit()	--ec618使用固定内部地址,所以不需要参数了
 */
 static int l_fota_init(lua_State* L)
 {