跳转到主要内容
x

基于智能液晶显示模块的水质监测设备

什么是水质监测设备?

它是利用光学传感器、离子选择性传感器对水质进行检测,并将结果清晰展示在液晶屏幕上,可以通过触摸屏幕操作和控制。该设备被广泛的应用于地表水、工业污水、饮用水源水质监测、调查和筛选领域。其优点是操作简便,显示结果清晰直观,除了实时的显示,还可以形成记忆曲线,用于长期的跟踪和研究。

本项目是基于Topway的7寸智能液晶显示模块,型号HMT070ETD-1D

分辨率:1024x600
接口:RS232 RJ45
工作电压:11V~26V
工作温度:-20°C ~ 70°C
宽视角:是
外形尺寸:185.9mm x 109.5mm x 25.4mm
尺寸:7"
视窗 :154.21mm x 85.92mm
触摸屏:CTP

一、项目需求分析:

1.    采集数据(定时采集水体的PH值和温度,以及环境的温湿度)
2.    显示在屏幕上(将采集的值实时显示在屏幕上)
3.    曲线展示(以曲线的形式将一段时间内的变量值显示在屏幕上)

二、项目流程

1、屏幕内容设计

1)一张背景图作为显示时间、日期和实时数据的主页

2)2个图标和2个坐标轴作为水质曲线页面

3)2个图标和2个坐标轴作为温度和湿度的显示页面

4)使用TOPWAY SGTools工具创建工程

导入图片和图标

5)选择字库,菜单栏中的工具-字体设置中可以选择合适的字库

6)创建所有页面

控件使用情况如下:

7)编辑图标

调整控件可以使用工具栏中的对齐工具,可以拯救强迫症患者

8)添加变量并绑定编辑

根据实际使用情况新建变量,这里只使用了16位数字变量和曲线变量

在对应的控件属性VP地址里绑定对应的变量的地址,同时也可以修改颜色大小等。

9)添加触摸键

10)使用TOPWAY的屏内RTC时钟功能, 并使用"RTC键盘(PIP)”替代,让开发设计更加便捷。

2、硬件设计

3、软件设计

1) 编写LCD 驱动程序

根据协议内容,编写16位数字变量的动

/**
brief LCD发送16位变量
param adr:变量地址
param dt:发送的变量
return 无
*/
voidlcd_send_pv16(uint32_tadr,uint16_tdt)
{
    uint8_tsend_buf[32];
    uint8_tidx = 0;

    send_buf[idx++] = 0xaa;
    send_buf[idx++] = 0x3d;
    adr = ntohl(adr);//大小端转换
    memcpy((uint8_t*)&send_buf[idx], (uint8_t*)&adr, sizeof(uint32_t));
    idx += sizeof(uint32_t);

    dt = ntohs(dt);//大小端转换
    memcpy((uint8_t*)&send_buf[idx], (uint8_t*)&dt, 2);
    idx += 2;
    send_buf[idx++] = 0xcc;
    send_buf[idx++] = 0x33;
    send_buf[idx++] = 0xc3;
    send_buf[idx++] = 0x3c;
    send_data1(send_buf, idx);
}


LCD左推进写曲线数据

/**
brief LCD左推进写曲线数据
param adr:变量地址
param adr_curve:插入位置
param dt:发送的变量
return 无
*/
voidlcd_send_curve(uint32_tadr,uint16_tadr_curve,uint16_tdt)
{
    uint8_tsend_buf[64];
    uint8_tidx = 0;

    send_buf[idx++] = 0xaa;
    send_buf[idx++] = 0x4e;
    adr = ntohl(adr);
    memcpy((uint8_t*)&send_buf[idx], (uint8_t*)&adr, sizeof(uint32_t));
    idx += sizeof(uint32_t);

    adr_curve = ntohs(adr_curve);
    memcpy((uint8_t*)&send_buf[idx], (uint8_t*)&adr_curve, sizeof(uint16_t));
    idx += sizeof(uint16_t);

    dt = ntohs(dt);
    memcpy((uint8_t*)&send_buf[idx], (uint8_t*)&dt, sizeof(uint16_t));
    idx += sizeof(uint16_t);

    send_buf[idx++] = 0xcc;
    send_buf[idx++] = 0x33;
    send_buf[idx++] = 0xc3;
    send_buf[idx++] = 0x3c;
    send_data1(send_buf, idx);
}
 


跳转页面的驱动

/**
brief LCD跳转页面
param page:页面地址
return 无
*/
voidlcd_send_page(uint16_tpage)
{
    uint8_tsend_buf[64];
    uint8_tidx = 0;

    send_buf[idx++] = 0xaa;
    send_buf[idx++] = 0x70;
    page = ntohs(page);
    memcpy((uint8_t*)&send_buf[idx], (uint8_t*)&page, sizeof(uint16_t));
    idx += sizeof(uint16_t);
    send_buf[idx++] = 0xcc;
    send_buf[idx++] = 0x33;
    send_buf[idx++] = 0xc3;
    send_buf[idx++] = 0x3c;
    send_data1(send_buf, idx);
}
 


设置时间的驱动

/**
brief LCD设置时间
param page 无
return 无
*/
voidlcd_send_time(void)
{
    uint8_tsend_buf[64];
    uint8_tidx = 0;

    send_buf[idx++] = 0xaa;
    send_buf[idx++] = 0x9c;

    memcpy((uint8_t*)&send_buf[idx], (uint8_t*)&my_rtc, sizeof(MYRTC));
    idx += sizeof(MYRTC);

    send_buf[idx++] = 0xcc;
    send_buf[idx++] = 0x33;
    send_buf[idx++] = 0xc3;
    send_buf[idx++] = 0x3c;
    send_data1(send_buf, idx);
}
 


读取时间的驱动

/**
brief LCD读取时间
param page 无
return 无
*/
voidlcd_read_time(void)
{
    uint8_tsend_buf[64];
    uint8_tidx = 0;

    send_buf[idx++] = 0xaa;
    send_buf[idx++] = 0x9b;
    send_buf[idx++] = 0xcc;
    send_buf[idx++] = 0x33;
    send_buf[idx++] = 0xc3;
    send_buf[idx++] = 0x3c;
    send_data1(send_buf, idx);
}
 


接收LCD数据的程序

voidlcd_pro(void)
{
    staticuint32_ttimer_lcd = 0;
    if (timer6 - timer_lcd<9)
        return;
    timer_lcd = timer6;
    if (lcd_rx.len<5)
        return;
    if (lcd_rx.rx_buff[0] != 0xaa)
    {
        memset((uint8_t*)&lcd_rx.rx_buff, 0, lcd_rx.len);
        lcd_rx.len = 0;
        return;
    }
    else
    {
        switch (lcd_rx.rx_buff[1])
        {
        case0x79://触摸
            lcd.page = lcd_rx.rx_buff[3];
            lcd.touch_id = lcd_rx.rx_buff[4];
            break;
        case0x77://下发数据
            if(lcd_rx.rx_buff[3] == 0x08)
            {
                if(lcd_rx.rx_buff[5] == 0x0A)//年
                {
                    my_rtc.year = lcd_rx.rx_buff[7];
                }
                elseif(lcd_rx.rx_buff[5] == 0x0C)//月
                {
                    my_rtc.month = lcd_rx.rx_buff[7];
                }
                elseif(lcd_rx.rx_buff[5] == 0x0E)//日
                {
                    my_rtc.day = lcd_rx.rx_buff[7];
                }
                elseif(lcd_rx.rx_buff[5] == 0x10)//时
                {
                    my_rtc.hour = lcd_rx.rx_buff[7];
                }
                elseif(lcd_rx.rx_buff[5] == 0x12)//分
                {
                    my_rtc.min = lcd_rx.rx_buff[7];
                }
                elseif(lcd_rx.rx_buff[5] == 0x14)//秒
                {
                    my_rtc.sec = lcd_rx.rx_buff[7];
                }
                lcd_send_time();
            }
            break;
        case0x9B://读取时间
            memcpy((uint8_t*)&my_rtc, (uint8_t*)&lcd_rx.rx_buff[2], sizeof(MYRTC));
            break;
        default:
            break;
        }
        memset((uint8_t*)&lcd_rx.rx_buff, 0, lcd_rx.len);
        lcd_rx.len = 0;
    }
}
 


2) PH值采集

这里用的传感器是模拟信号,可以直接使用单片机的AD去采集值,转化为电压之后,根据拟合后的公式直接转换为PH值,程序如下

voidph_pro(void)
{
  staticuint32_ttimer_ph = 0;
  uint16_tadc_value = 0;
  floatvol = 0.0;
  if (timer6 - timer_ph>PH_COLLECT_TIME)
  {
    HAL_ADC_Start(&hadc1);                  //启动ADC单次转换
    HAL_ADC_PollForConversion(&hadc1, 50);  //等待ADC转换完成
    if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
    {
      adc_value = HAL_ADC_GetValue(&hadc1);   //读取ADC转换数据
      vol = ((double)adc_value/4096)*3.3*2;//转化为实际电压值
      printf("sensor log:adc_value = %d, vol = %.2fV.\n", adc_value, vol);
      ph_buf[FILTER_N] = (uint16_t)((5.9647*vol+22.255)*10);//转换为PH值
      for (i = 0; i <FILTER_N; i++)
      {
          ph_buf[i] = ph_buf[i + 1];   // 所有数据左移,低位仍掉
      }
      pv.ph = MedianFilter(ph_buf, FILTER_N); //中值平均滤波
      printf("sensor log:ph %d\r\n", pv.ph);
    }
    lcd_send_pv16(0x080000,pv.ph);//给LCD发送数据
    lcd_send_curve(0x060000,224,pv.ph);//给LCD左推进发送曲线
    timer_ph += PH_COLLECT_TIME;
  }
}
 


3) 水温数据采集

使用的是经典的DS18B20,单总线获取温度,程序如下

voidds18b20_pro(void)
{
  uint16_ti = 0;
  staticuint32_ttimer_ds18b20 = 0;
  if (timer6 - timer_ds18b20>DS_COLLECT_TIME)
  {
    te_buf[FILTER_N] = DS18B20_Get_Temp();
    for (i = 0; i<FILTER_N; i++)
    {
        te_buf[i] = te_buf[i + 1];   // 所有数据左移,低位仍掉
    }
    pv.te = MedianFilter(te_buf, FILTER_N); //中值平均滤波
    lcd_send_pv16(0x080002, pv.te);//给LCD发送数据
    lcd_send_curve(0x0600e0,224,pv.te+100);//给LCD左推进发送曲线
    printf("sensor log:te %d\r\n", pv.te);
    timer_ds18b20 += DS_COLLECT_TIME;
  }
 


4) 环境温度数据采集

使用的是国产的AHT10,IIC协议获取温湿度,程序如下

voidaht_pro(void)
{
  uint16_ti = 0;
  staticuint32_ttimer_aht = 0;
  if (timer6 - timer_aht>AHT_COLLECT_TIME)
  {
    AHT10ReadData(&tem_buf[FILTER_N], &hum_buf[FILTER_N]);
    for (i = 0; i<FILTER_N; i++)
    {
        hum_buf[i] = hum_buf[i + 1];   // 所有数据左移,低位仍掉
        tem_buf[i] = tem_buf[i + 1];   // 所有数据左移,低位仍掉
    }
    pv.tem = MedianFilter(tem_buf, FILTER_N); //中值平均滤波
    pv.hum = MedianFilter(hum_buf, FILTER_N); //中值平均滤波

    printf("sensor log:tem %d\r\n", pv.tem);
    printf("sensor log:hum %d\r\n", pv.hum);
    

    lcd_send_curve(0x0602a0,224,pv.tem+100);//给LCD左推进发送曲线
    lcd_send_curve(0x0601c0,224,pv.hum);//给LCD左推进发送曲线
    
    lcd_send_pv16(0x080004,pv.hum);//给LCD发送数据
    lcd_send_pv16(0x080006,pv.tem);//给LCD发送数据

    timer_aht += AHT_COLLECT_TIME;
  }
}
 


5)    LCD操作流程图

4.测试

1) 主页

2) 水质曲线界面

3) 环境温湿度曲线界面

5、总结

本项目用到的硬件:NUCLEO-G070RB及硬件扩展板、PH温度采集传感器、Topway智能显示模块HMT070ETD-1D

开发工具及版本:TOPWAY SGTools V9.35、STM32CuBeMx V6.4.0 、KEILMDK V5.25、PhotoShop。

其中TOPWAY智能显示屏HMT070ETD-1D让工程师摆脱繁琐的UI和触摸屏编程,主机只需要通过简单的串口命令把数据传输到智能显示屏,屏幕把显示、接收用户输入等工作全部接管过来。编程,它具有如下产品特点:
支持宽电压供电工作;
同时支持PC下载和U盘升级两种模式;
支持远程协议通信控制,远程显示工程升级;
支持电容屏支持多功能触摸按键;
支持Lua脚本多功能开发;
使用SGTools工具便捷使得开发过程十分简便。