上次我们基于小熊派光强传感器项目实现了光强读取并在LCD上显示,文章链接如下:
基于小熊派光强传感器BH1750状态机驱动项目升级(带LCD屏显示)
这一节,我们再次对这个项目升级下,配个带可缩放曲线的上位机读取光强进行显示吧!
本节,你将了解工作中上位机和MCU的是如何来配合使用的。
在工作中,我们常常需要对一些传感器的某些数值进行长时间的测试和观察,以了解传感器的性能,在电子工程里,我们经常听到的测试曲线莫过于电池充放电曲线了,通过电池充放电曲线,我们很容易可以知道电池在实际使用过程中满电和馈电的状态以及电池的使用周期等等,今晚,我们就让光强通过曲线显示出来,用QT+QCustomPlot来实现,最终效果如下视频操作所示:
一、QCustomPlot简介
以下是QCustomPlot的官网:
https://www.qcustomplot.com/
QCustomPlot是一个小型的Qt画图标类,支持绘制静态曲线、动态曲线、多重坐标曲线,柱状图,蜡烛图等。只需要在项目中加入头文件qcustomplot.h和qcustomplot.cpp文件,然后使一个widget提升为QCustomPlot类,即可使用。
二、更改上节的MCU端程序
这次,我们选用串口和上位机进行通信,所以我们需要设计一个传感器和上位机通信的协议,协议如下:
序号 光强值 \r\n
当序号大于等于65535时,自动清0。
修改上节的main函数为如下:
/** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /*流水号*/ int serial_number = 0; char display_buf[20] = {0}; char procol_buf[20] = {0}; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C1_Init(); MX_USART1_UART_Init(); MX_SPI2_Init(); /* USER CODE BEGIN 2 */ /*串口初始化后加这个延时,防止后面的printf打印乱码*/ HAL_Delay(200); LCD_Init(); LCD_Clear(BLACK);//清屏为黑色 LCD_ShowString(5, 10, 240, 32, 32, "BearPi LuxTest");//显示字符串,字体大小32*32 Init_BH750(); timer_init(&lsensor.timer1, lsensor.timeout_cb, 1, 1); timer_start(&lsensor.timer1); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ ReadBH1750(LUX_1_MODE); if(1 == lsensor.Conver_completed) { ++serial_number ; if(65535 == serial_number) serial_number = 0 ; sprintf(display_buf, "%d%d%d%dLux", lsensor.Lux / 1000 % 100, lsensor.Lux / 100 % 10, lsensor.Lux / 10 % 10, lsensor.Lux % 10); LCD_ShowString(80, 50 + 24 + 32, 240, 32, 32, display_buf); //显示字符串,字体大小32*32 if(lsensor.Lux > LIGHT_SENSOR_THREHOLD) { HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); } sprintf(procol_buf, "%d%d%d%d%d %d%d%d%d%d", serial_number / 10000, serial_number / 1000 % 100 % 10, serial_number / 100 % 10, serial_number / 10 % 10, serial_number % 10, lsensor.Lux / 10000, lsensor.Lux / 1000 % 100 % 10, lsensor.Lux / 100 % 10, lsensor.Lux / 10 % 10, lsensor.Lux % 10 ); printf("%s \r\n", procol_buf); } timer_loop(); } /* USER CODE END 3 */ }
下载后,打开串口调试助手可以看到:
三、光强曲线显示上位机应用开发
下面我先用QT画出一个简单的界面(已经将QCustomPlot用上了),如下:
这里我们需要使用QT5的串口库,还有QCustomPlot库,所以在.pro中需要添加对应的库:
#------------------------------------------------- # # Project created by QtCreator 2020-04-13T20:46:41 # #------------------------------------------------- QT += core gui serialport greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport TARGET = BearPi_QT TEMPLATE = app RC_FILE += app.rc # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mainwindow.cpp \ qcustomplot.cpp HEADERS += \ mainwindow.h \ qcustomplot.h FORMS += \ mainwindow.ui
在mainwindow.h中,需要添加头文件及变量还有相关的普通函数和槽函数的定义:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QDebug> #include <QMainWindow> #include <QSerialPort> //提供访问串口的功能 #include <QSerialPortInfo> //提供系统中存在的串口的信息 #include "qcustomplot.h" //包含Qcustomplot库 namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); /*初始化串口参数*/ void Init_Serial_Para(); /*传感器数据画图前的处理*/ void sensor_data_preprocess(int x, int y) ; /*画曲线函数*/ void Sensor_Draw_Plot(QCustomPlot *customPlot, QVector<double> x, QVector<double> y); private: Ui::MainWindow *ui; QSerialPort *Serial ; long last_index ; QVector<double> display_x; QVector<double> display_y; QString Serial_Port , Serial_Bauardate,Serial_PariteyBit,Serial_DataBit,Serial_StopBit ; private slots: void Update_Serial_Port(QString Currnet_Select); void Update_Serial_Baurdrate(QString Currnet_Select); void Update_Serial_ParityBit(QString Currnet_Select); void Update_Serial_DataBit(QString Currnet_Select); void Update_Serial_StopBit(QString Currnet_Select); void on_Connect_Device_clicked(); void on_Disconnect_Device_clicked(); void cmd_recv_process(); }; #endif // MAINWINDOW_H
在mainwindow.cpp太长我就不全贴出来了,解读一下核心的实现思路:
1、用户配置完串口参数,然后 连接信号与槽:
connect(this->Serial,SIGNAL(readyRead()),this,SLOT(cmd_recv_process()));
2、当上位机接受到MCU端发来的数据时,会触发readyRead()信号,进而调用cmd_recv_process()槽函数,实现如下:
/*数据接收*/ void MainWindow::cmd_recv_process() { QString data ; QStringList list ; /*光强传感器序号*/ QString Sensor_Serial_Number ; /*光强传感器光强值*/ QString Sensor_light_Value ; long int s1 = 0, v1 = 0; last_index =0; if(Serial->canReadLine()) { QByteArray temp = Serial->readAll(); if(!temp.isEmpty()) { data = temp ; list = data.split(" "); if(3 == list.length()) { Sensor_Serial_Number = list[0]; Sensor_light_Value = list[1]; /*取到对应参数后立刻开始画图*/ s1 = Sensor_Serial_Number.toInt(); v1 = Sensor_light_Value.toInt(); ui->light_sensor_value->setText(QString::number(v1)); if (last_index == 0 || (last_index < s1 && s1 - last_index >=1 && s1 - last_index <= 10 )) { last_index = s1; sensor_data_preprocess(s1,v1); } } } } }
这里会判断是否满足读取一行数据的条件,如果满足则会读取一行数据,接下来对数据进行分割,取出传感器上报的序号、光强两个字段,序号作为曲线图的横坐标,光强作为曲线图的纵坐标进行显示。
例程下载
链接:https://pan.baidu.com/s/1ujo0TE3pS-1RFVvylnCyKQ 提取码:48jp 复制这段内容后打开百度网盘手机App,操作更方便哦
往期精彩
超轻量级网红软件定时器multi_timer(51+stm32双平台实战)
基于小熊派光强传感器BH1750实践(multi_timer+状态机工程应用)