8 Flash API读写
大彩串口用户Flash掉电存储空间一共128K,地址范围为0~131072(10进制,128*1024),其中和modbus掉电存储、历史曲线、数据记录控件、LUA API Flahs掉电存储、大彩指令Flash 指令均是共用这128K存储地址,使用flash掉电存储需要注意以下问题
1.地址重叠:在这128K的存储中,以下情况需要两两排查是否重叠
- 历史曲线开启存储(非块地址存储):应排查历史曲线控件所占用的存储大小,以免和Lua脚本中的 Flash 操作冲突
- 数据记录控件开启存储(非块地址存储):应排查数据记录控件所占用的存储大小,以免和Lua脚本中的 Flash 操作冲突
- modbus协议:开启存储,Lua脚本中的 Flash 存储地址需要避开0~2047
- 大彩协议指令存储:排查主板发送Flash存储指令的地址,以免和Lua脚本中的 Flash 操作冲突
2.擦写次数:
- 此区域的擦写次数为10W次左右,使用存储的时候应该斟酌使用,忌讳变量每秒存储一次的操作。建议存储一些关键变量,在变量变化存储,以延长Flash的寿命
3.虚拟屏不支持‘掉电存储’,请在实体屏运行、测试
Flash API 如下所示
1 write_flash(addr,data)
写用户FLASH数据,addr写入地址,data字节数组,下标从0开始。
2 read_flash(addr,length)
读用户FLASH数据,addr写入地址,length读取字节数,返回类型为字节数组,下标从0开始
3 write_flash_string(addr,str)
写字符串到指定FLASH地址
4 read_flash_string(addr)
从指定FLASH地址读取字符串,成功返回字符串,失败返回nil
5 flush_flash()
系统会对NAND FLASH写入操作进行缓存优化,以提高写入效率
flush_flash操作会立即把数据写入FLASH
6 flush_nor()
系统会对NOR FLASH写入操作进行缓存优化,以提高写入效率
flush_nor操作会立即把数据写入FLASH
本例程主要讲述以下几点存储方式
- 字节数组存储
- 字符串存储
- 字符串JSON格式存储
适用范围:M系列、W系列、X系列、F系列(固件版本 >= V4.2.401.0)
例程下载链接:《Flash API读写》(点击跳转)
8.1 字节数组存储
假设从0地址开始存储两个变量A,B,其中变量A的范围为0~255,变量B的范围为0~65535,则存储共需要3个字节,假设A=80,B=1000,为了方便理解,此处value用16进制表示,内部存放如下所示
addr | 0 | 1 | 2 |
---|---|---|---|
value | 0x50 | 0x03 | 0xE8 |
根据上表可知,每一个地址只存储一个字节,即相对于字符串,对比而言存在以下优缺点
优点:省空间
缺点:变量连续地址存储不利于读写、需要对数组下标操作
- 变量为整形:若value >= 255, 读写过程需要对数值移位操作,如下所示
--写操作
local wA = 80
local wB = 1000
local wdata = {}
wdata [0] = A
wdata [1] = (B >> 8) & 0xFF --移位赋值字节数组
wdata [2] = (B >> 0) & 0xFF --移位赋值字节数组
write_flash(0, wdata)
假设已知存储的A、B变量在地址0,共3个字节:
--读操作
local rdata = read_flash(0,3)
lcoal rA = rdata[0]
lcoal rB = (rdata[1] << 8) | rdata[2] --拼接字节数组
- 变量为字符串:读取出来的数值需要转为字符拼接,如下所示
--写操作
local wString = "中国"
lcoal wdata = {}
for i = 1,string.len(wString)
do
wdata [i-1] = string.byte(wString, i, i) --转为字节,并赋值到数组
end
write_flash(0, wdata[0])
-- wdata[0] = 0xD6
-- wdata[1] = 0xD0
-- wdata[2] = 0x87
-- wdata[3] = 0xEB
假设已知存储字符串变量为“中国”,共4个字节:
--读操作
local rdata = read_flash(0,4)
local rString = ‘’
for i = 0,#(rdata)
do
rString = rString..string.char(rdata[i]) --转为字符,并拼接
end
--rString = '中国'
画面配置
画面中添加以下控件
文本控件ID3、ID5:输入方式为键盘输入,其中ID3的数值限定为0~255,ID5的数值限定为0~65535
文本控件ID8、ID10:输入方式为用户主机输入
按钮控件ID1:作为写操作的触发按钮
按钮控件ID6:作为读操作的触发按钮,如下所示
LUA脚本
已知道上小节画面配置了控件ID3占用一个字节,控件ID5的值占用2个字节,当点击按钮控件ID1的时候,获取文本控件ID3、ID5的值,并写入flash。当点击按钮控件6,读取Flash里的数据,并显示出来
local sc_byte = 0
local byte_addr = 0x00000000
--用户通过触摸修改控件后,执行此回调函数
--点击按钮控件,修改文本控件、修改滑动条都会触发此事件
function on_control_notify(screen,control,value)
if screen == sc_byte
then
if control == 1 and value == 0
then
local write_byte_data = {}
write_byte_data[0] = get_value(sc_byte,3)
write_byte_data[1] = ( get_value(0,5) >> 8) & 0xFF
write_byte_data[2] = get_value(0,5) & 0xFF
flush_flash()
write_flash(0,write_byte_data)
elseif control == 6 and value == 0
then
local read_byte_data = {}
read_byte_data = read_flash(byte_addr,3)
set_text(0, 8, read_byte_data[0])
set_text(0, 10, (read_byte_data[1]<<8) | read_byte_data[2])
end
......
end
运行预览
该Flash掉电存储需要在实体屏体验,本例程中,通过修改文本值点击write按钮保存,然后断电重启后,点击read按钮,将数据读取出来显示。本例程为了方便演示、说明,实际应用中,把读flash操作放在on_init()中,屏幕上电自动读取
8.2 字符串读写
假设从0地址开始存储两个变量sA,sB,其中变量A=‘abc’,变量B=‘中国’,则将变量A、B拼接起来,并用分隔符隔开,即是
w_AB = A .. ‘,’ .. ’B’ -–w_AB = ‘abc,中国’
为了方便理解,此处value用16进制表示,内部存放如下所示
addr | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
value | 0x61 | 0x62 | 0x63 | 0x2C | 0xD6 | 0xD0 | 0x87 | 0xEB |
根据上表可知,每一个地址只存储一个字节,即相对于字节存储,对比而言存在以下优缺点
优点:对字符串存储友好
缺点:对数值存储占用空间,如A=200(0<=A<255),字节存储仅占一个字节,0xC8,若字符串需要占用3个字节,0x32 0x30 0x30
画面配置
画面中添加以下控件
文本控件ID3、ID5:输入方式为键盘输入,用于修改要保存的到Flash的数据
文本控件ID8、ID10:输入方式为用户主机输入,用于显示从Flash读取的数据
按钮控件ID1:作为写操作的触发按钮
按钮控件ID6:作为读操作的触发按钮,如下所示
LUA脚本
已知道上小节画面配置了控件ID3、控件ID5均用于输入字符串,当点击按钮控件ID1的时候,获取文本控件ID3、ID5的值,拼接“,”后写入flash。当点击按钮控件6时,读取Flash里的数据,检索“,”并调用split()[自定义函数]分割,最后显示在对应文本控件ID8、ID10中,代码如下所示
local sc_str = 1
local str_addr = 0x00000064
local token = ','
-- 切割字符串,遇到pat符号就切割
-- 自定义函数(非回调函数,可以修改)
-- str 需要切割的字符串
-- pat 遇到此字符切割
function split(str, pat)
local t = {}
local last_end = 0
local s, e = string.find(str, pat, 1) -- 第一个分割号的位置
while s
do
-- 找出分割符的前的参数
table.insert(t, string.sub(str, last_end + 1, last_end + s - last_end - 1))
last_end = e
s, e = string.find(str, pat, last_end + 1)
end
if last_end <= #str then
cap = string.sub(str, last_end + 1)
table.insert(t, cap)
end
return t
end
--用户通过触摸修改控件后,执行此回调函数。
--点击按钮控件,修改文本控件、修改滑动条都会触发此事件。
function on_control_notify(screen,control,value)
......
elseif screen == sc_str
then
if control == 1 and value == 0
then
local write_Jsonstr_data = {}
write_Jsonstr_data[0] = get_text(sc_str, 3)
write_Jsonstr_data[1] = get_text(sc_str, 5)
local write_str = write_Jsonstr_data[0]..token..write_Jsonstr_data[1]
print("Dbug : write_str -> "..write_str)
flush_flash()
write_flash_string(str_addr,write_str)
elseif control == 6 and value == 0
then
local read_str = ''
local read_str_data = {}
read_str = read_flash_string(str_addr)
print("Dbug : read_str -> "..read_str)
read_str_data = split(read_str, token)
set_text(sc_str, 8, read_str_data[1])
set_text(sc_str, 10, read_str_data[2])
end
......
end
end
运行预览
该Flash掉电存储需要在实体屏体验
8.3 字符串存储-JSON格式
对象可以包含多个 key/value(键/值)对,key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null),JSON同时具有自我描述性,更易理解,一般声明数组,通过函数压缩成JSON字符串,或将JSON字符串解压成数组,常见声明有以下方式:
--声明一个数组
local addNewPrj_Tb = {
addPercentage = 0,
description = "水果",
duration = 0,
name = "苹果",
percentage = 0,
price = 11,
selectPercentage = 0,
type = 1,
wechatShow = false
}
若引用name的值,其中“name”为key,“苹果”为value
print(jsonTest[name])
print(jsonTest.name)
>大彩
>大彩
- 表压缩为JSON字符串
local newPrj = cjson.encode(addNewPrj_Tb)
print(newPrj)
> {"selectPercentage":0,"addPercentage":0,"description":"水果","name":"苹果","duration":0,"wechatShow":true,"type":1,"price":11,"percentage":0}
- JSON字符串解压成表,即是“jsonStr”字符串被解压成“newTb”表,就可以引用里面成员,如newTb[price]
--JSON字符串解压成
local jsonStr = {"selectPercentage":0,"addPercentage":0,"description":"水果","name":"苹果","duration":0,"wechatShow":true,"type":1,"price":11,"percentage":0}
local newTb = cjson.dncode(jsonStr)
print(newTb[price])
>11
根据上表可知,每一个地址只存储一个字节,即相对于上述字节存储、字符串存储,对比而言,字符串存储-JSON格式存在以下优缺点
优点:对字符串存储友好,对于连续变量无需分割,API一键压缩/解压,更方便引用成员,拥有自我描述性,项目中的代码可读性更强,更易理解
缺点:因为存在键值对,占用内存比上述两者方式都大
画面配置
画面中添加以下控件
文本控件ID3、ID5:输入方式为键盘输入,用于键潘修改后保存的到Flash
文本控件ID8、ID10:输入方式为用户主机输入,用于显示从Flash读取的数据
按钮控件ID1:作为写操作的触发按钮
按钮控件ID6:作为读操作的触发按钮,如下所示
LUA脚本
已知道上小节画面配置了控件ID3、控件ID5均用于输入字符串,当点击按钮控件ID1的时候,获取文本控件ID3、ID5的值,赋值给表压缩成JSON格式,在写入Flash。当点击按钮控件6时,读取Flash里的数据,通过解压JSON字符串,生成一个新的表,最后显示在对应文本控件ID8、ID10中,代码如下所示
local sc_Jsonstr = 2
local jsonstr_addr= 0x000000C8
--用户通过触摸修改控件后,执行此回调函数。
--点击按钮控件,修改文本控件、修改滑动条都会触发此事件。
function on_control_notify(screen,control,value)
......
elseif screen == sc_Jsonstr
then
if control == 1 and value == 0
then
local write_Jsonstr_data = {}
write_Jsonstr_data["data1"] = get_text(sc_Jsonstr, 3)
write_Jsonstr_data["data2"] = get_text(sc_Jsonstr, 5)
local write_jsonStr = cjson.encode(write_Jsonstr_data)
flush_flash()
write_flash_string(jsonstr_addr, write_jsonStr)
elseif control == 6 and value == 0
then
local read_Jsonstr_flash = {}
local read_Jsonstr = read_flash_string(jsonstr_addr)
read_Jsonstr_flash = cjson.decode(read_Jsonstr)
print(read_Jsonstr_flash["data1"])
print(read_Jsonstr_flash["data2"])
set_text(sc_Jsonstr, 8, read_Jsonstr_flash["data1"])
set_text(sc_Jsonstr, 10, read_Jsonstr_flash["data2"])
end
end
end
运行预览
该Flash掉电存储需要在实体屏体验