制定页面结构
在制作一个产品UI之前,你需要先想好该产品页面的大致结构和逻辑关系,比如在本产品中,我在设计的时候,手绘了一个简单的逻辑图:
可以看到,整个系统分为5个页面,于是我们得到了页面列表:
页面规划
页面编号 | 名称 | 功能描述 |
---|---|---|
0 | logo | 启动界面,2秒后跳转到大字页面 |
1 | 大字界面 | 显示数字单路,有菜单按钮跳菜单 |
2 | 曲线界面 | 左字右曲线,有菜单按钮 |
3 | 菜单界面 | 校准、大字界面、曲线界面 |
4 | 校准界面 | 实时显示串口传入的AD值,并由4个按钮,记录4个校准值 |
校准和计算AD值的代码
可以看出,上面5个规划页面在sHMIctrl体系中,使用控件是非常好完成的一件事情,而相反,对于AD值计算,却需要写一段程序来完成:
首先,我们回忆一下前面的方案图:
可以看到,在数据层面,系统分为下面两个部分 - 1、校准值的存储 - 2、AD值到显示值的计算
校准值的存储
sHMIctrl控制器提供了数据绑定和数据存储,详见数据存储 ;
因此我们可以把校准值存到这个地方,为此我们需要将校准值和变量列出来:
变量名 | 类型 | 说明 |
---|---|---|
X5V | int,存储 | 5V时AD值 |
XLA | int,存储 | 200ma时AD值 |
XHA | int,存储 | 2A时AD值 |
XZA | int,存储 | 0ma时AD值 |
电压 | int | 当前电压值,单位mV |
电流 | int | 当前电流值,单位mA |
可以看到,前面4个是校准值,需要固化存储,而后面2个是显示值,无需存储;
在工程文件这里设置,就可以初始化这4个变量;
AD值到显示值的计算
可以看到,AD值通过串口传入:
app.data("AD1|AD2");\r\n
这条语句的时候,我们并不能确定当前正在显示的是哪个页面,因此这个转换函数应该是全局有效,因此我们需要放在app控件中,为什么需要放在app控件中,详见:基本页面详细讲解
因此,在上位机开发环境中,选择“控件”-->"编辑全局控件appbase",即可进入appbase的控件代码:
首先,我们需要增加该控件的属性:
str sv="0.00"; //电压显示字符串
str sa="0.00"; //电流显示字符串
int vv=0; //电压:单位mv;
int va=0; //电流:单位ma;
int ad1=0; //电压ad
int ad2=0; //电流ad
可以看到ad1、ad2是原始采集值;vv、va是计算好的显示值,但是由于系统不支持浮点数,因此需要sv,sa字符串变量来存储显示值;
然后,再书写对应的处理函数:
void datawork(){} //页面事件
void da(str s){
//由串口cmd调用 app.data("AD1|AD2|");
int adh;
int adl;
ad1=explode_num(s,"|",0);
ad2=explode_num(s,"|",1);
//----计算 电压电流值
adh=bind_get("X5V");
if (adh>0)vv=5000*ad1/adh;
adl=bind_get("XLA");
adh=bind_get("XHA");
if((adh-adl)!=0) {va=1800*(ad2-adl)/(adh-adl)+200;}
//---合成字符串,规则10VA一下,3位小数;10以上,2位小数,保证显示字符串含小数点5位
if (vv>10000){
sv=itos(vv,4,2);
}
else{
sv=itos(vv,4,3);
}
if (va>10000){
sa=itos(va,4,2);
}
else{
sa=itos(va,4,3);
}
//---后续处理
bind_set("电压",sv);
bind_set("电流",sa);
datawork();
}
代码很简单,由于记录了5V时AD的值,因此采集到的AD值,对比一下5V时AD的值,就可以按比例计算出当前AD对应的电压值;电流值的计算方法基本一致,但是考虑到运放有一个基础值的问题,也就是0A电流时,运放输出的电压不为0,因此我们校准的时候使用了200ma和2A这两个值来进行比值计算;0A电流校准值这里没有使用,预留;
为此,表头的核心算法已经完成,任何界面只要绑定“电压”和“电流”两个数据绑定项,即可完成实时显示;
开始制作页面
logo页面
“进入”按钮选xk_button控件,在status中,选择“定时器计数值”为200,也就是200*10ms=2000ms,也就是2秒产生一次定时中断,然后在页面程序中增加:
void x1.ontimer()
{
gopage(1);
}
也就是说logo界面显示2秒后,产生定时中断,跳转到第一页;
大字页面
以下是核心代码:
class vol=loadcom(label);
class cur=loadcom(label);
bind(vol.text,"电压");
bind(cur.text,"电流");
start();
也就是说放两个Lable到界面,并且绑定电压电流两个数据绑定项,即可完成显示,因此这个界面可以灵活设计;
曲线界面
这个界面需要使用高级曲线控件,该控件的详细说明参见:高级曲线控件 这个曲线控件主要需要设置自动量程标尺,这个稍微有点不好理解,我们来讲述一下:
控件放在页面上,我们可以看到这个屏幕的分辨率显示7格效果是比较好的,此时我们可以看到,7格的曲线,需要标注6个标尺值;
对于电压来说,36V的量程,折算成数值:36000(app.vv属性单位是mv); 因此我们可以设置多档量程:3V,6V,12V,18V,24V,30V,36V;按照设置规则,于是我们得出电压量程字符串:
3000|3.0V|2.5V|2.0V|1.5V|1.0V|0.5V
6000|6.0V|5.0V|4.0V|3.0V|2.0V|1.0V
12000|12V|10V|8.0V|6.0V|4.0V|2.0V
18000|18V|15V|12V|9.0V|6.0V|3.0V
24000|24V|20V|16V|12V|8.0V|4.0V
30000|30V|25V|20V|15V|10V|5.0V
36000|36V|30V|24V|18V|12V|6.0V
同样电流最大量产5A,数值5000,按照设置可以分为:300MA、600mA、1200mA、2400mA、4800mA、6000mA,于是得到电流量程字符串:
300|300mA|250mA|200mA|150mA|100mA|50mA
600|600mA|500mA|400mA|300mA|200mA|100mA
1200|1.4A|1.2A|1.0A|800mA|400mA|200mA
2400|2.4A|2.0A|1.6A|1.2A|800mA|400mA
4800|4.8A|4.0A|3.2A|2.4A|1.6A|800mA
6000|6.0A|5.0A|4.0A|3.0A|2.0A|1.0A
同时在页面中需要添加c1曲线控件和app全局控件的继承事件:
void c1.create()
{ int i;
super.create(); //计算max=x2-x1
c1.t1=newbin(c1.max*2); //启动第一条曲线
c1.t2=newbin(c1.max*2); //启动第一条曲线
}
void app.datawork(){
c1.set(app.vv,app.va,0,0);
}
c1.creat事件参见curve2控件,create中主要初始化控件的曲线,这个例子中我们初始化了2条曲线;
前面的代码可以看到,外部每调用一次app.da()函数送入测量新值,app.da()最后会调用datawork()事件;在其他页面这个事件基本无用,但是在曲线界面,需要驱动曲线推进,因此我们需要继承这个事件,让c1完成一次数据输入,也就是曲线推进;
做成的界面:
菜单界面
这个基本上不用讲,设置几个图标的事;
校准界面
这个界面是一个灵活应用界面,首先需要放置一个表格控件,表格控件的使用说明详见:表格控件
需要设置以下属性参数:
tcol:100|60|80
trow:20|20|20|30|30|30|30
这两个参数可以确定表格的行宽和列高
cell:
变量|值|操作
电压AD值|2048|2.500V
电流AD值|300|0.300A
5V时AD值|--|@
0A时AD值|--|@
0.2A时AD值|--|@
2A时AD值|--|@
cell设置表格内容,@表示本格放置其他控件,在本项目中,放置了按钮;
下面,我们看下对应的页面程序:
void reshow(){
t1.setcell(1,3,bind_get("X5V"));
t1.setcell(1,4,bind_get("XZA"));
t1.setcell(1,5,bind_get("XLA"));
t1.setcell(1,6,bind_get("XHA"));
}
void app.datawork(){
t1.setcell(1,1,app.ad1);
t1.setcell(1,2,app.ad2);
t1.setcell(2,1,app.sv);
t1.setcell(2,2,app.sa);
}
void x1.onclick()
{ //校准5V
bind_set("X5V",app.ad1);
memsave();
reshow();
}
reshow();
start();
这个程序只列出了核心代码,也就是说,在start()之前,调用了reshow()函数,该函数完成了固化的校准值到t1表格数据的覆盖;因此当界面显示的时候,实际表格内的校准数据值也会被正常显示的;
以5V校准为例,当外部把5V基准电压加到Vin测量端后,单片机会调用app.da("5V基准值时的AD|0");这个全局控件方法,于是在app.datawork()页面继承事件中,数据会更新到表格中;此时当用户按钮5V校准按钮的时候,系统会执行x1.onclick()事件,该事件会把5V对应的AD,存储在绑定数据区,并通过memsave存储到flash中;最后再刷新一次界面;