制定页面结构

在制作一个产品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
5VAD值|--|@
0AAD值|--|@
0.2AAD值|--|@
2AAD值|--|@

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中;最后再刷新一次界面;