STM32单片机项目实例:基于TouchGFX的智能手表设计(8)底层驱动与UI进行关联
一、概述与代码添加
在TouchGFX中,从Model类执行与应用非UI部分(这里称为后端系统)的通信。后端系统是从UI接收事件和将事件输入UI的软件组件,例如采集传感器的新测量值。后端系统可作为单独的任务在同一MCU、单独的处理器、云模块或其他硬件上运行。从TouchGFX的角度来看,这并不十分重要,只要它是能够与之通信的组件。
使用的特定通信协议不受TouchGFX管理。它只提供一个在每个TouchGFX嘀嗒时间调用一次的函数,可以在其中处理需要的通信。
图 1-1 Model-View-Presenter和外部通信
Model 类是一个永远存在的单类,它有两个用途:
- 保存UI的状态信息。 在切换屏幕时,View和Presenter的分配会被清除,因此它们不能用于存储在屏幕转换时应当保留的信息。 为此,使用Model保存信息。
- 作为面向后端系统的接口,向/从当前活动屏幕发送事件。
Model类是自动设置的,具有指向当前活动Presenter的指针。当Model中发生变化时,将变化通知当前活动Presenter。这是通过应用中ModelListener接口中的方法来完成的。TouchGFX Designer生成的新应用将自动拥有可直接供UI使用的Model类。
采用STM32CubeMX配置TouchGFX后,需要打开TouchGFX 4.21.2 Designer软件重新生成以下代码,避免MDK编译报错。配置生成的代码中主要包含三个文件:Model.cpp、Model.hpp、ModelListener.hpp,在这个三个文件中添加与UI相关的数据交互接口。
Model.cpp文件:
#include <gui/model/Model.hpp>
#include <gui/model/ModelListener.hpp>
Model::Model() : modelListener(0)
{
}
void Model::tick()
{
}
Model.hpp文件:
#ifndef MODEL_HPP
#define MODEL_HPP
class ModelListener;
class Model
{
public:
Model();
void bind(ModelListener* listener)
{
modelListener = listener;
}
void tick();
protected:
ModelListener* modelListener;
};
#endif // MODEL_HPP
ModelListener.hpp文件:
#ifndef MODELLISTENER_HPP
#define MODELLISTENER_HPP
#include <gui/model/Model.hpp>
class ModelListener
{
public:
ModelListener() : model(0) {}
virtual ~ModelListener() {}
void bind(Model* m)
{
model = m;
}
protected:
Model* model;
};
#endif // MODELLISTENER_HPP
在Model.cpp中增加与底层驱动关联代码,Model.hpp与ModelListener.hpp用于函数的声明。下面是基于TouchGFX的智能手表设计的Model.cpp中的底层关联代码:
#include <gui/model/Model.hpp>
#include <gui/model/ModelListener.hpp>
#if defined LINK_HARDWARE //TuchGFX仿真与实际硬件操作隔离
//头文件包含
extern "C"
{
#include "user_app.h"
}
//底层数据
extern volatile SHT20_TemRH_Val gTemRH_Val;
extern RTC_DateTypeDef gSystemDate; //获取日期结构体
extern RTC_TimeTypeDef gSystemTime; //获取时间结构体
extern gTask_BitDef gTaskStateBit; //任务执行过程中使用到的标志位
extern gTask_MarkEN gTaskEnMark; //系统任务使能标识
extern volatile StruAP3216C_Val gAP3216C_Val; //AP3216数据结构
extern volatile uint8_t gLastTimeSeconds; //上一次的时间
extern volatile float pitch,roll,yaw; //欧拉角
extern unsigned long gSportStep; //运动步数
extern wifiRSSI ao_wifiRSSI;
extern uint8_t gFiveKeyFunc; //定义的五向按键值功能
extern volatile uint16_t gCurrentVal; //资源扩展板电流,通道IN8
extern volatile uint16_t gVoltageVal; //资源扩展板电压,通道IN9
extern volatile uint16_t gChipTempVal;//内部参考电压,通道IN12
extern volatile uint16_t gVrefVal; //内部参考电压,通道IN13
extern volatile uint16_t gVbatVal; //RTC电池电压,通道IN14
extern int32_t n_heart_rate; //heart rate value=n_heart_rate/4,采样率100sps,max30102设置4点求平均
extern int32_t n_sp02; //SPO2 value
extern int8_t ch_spo2_valid; //indicator to show if the SP02 calculation is valid
extern int8_t ch_hr_valid; //indicator to show if the heart rate calculation is valid
extern uint8_t gWiFiInfo[40]; //用于通知View界面的Text文本显示
//
volatile uint8_t gSwitchSpace = 0x00; //页面切换的时间间隙
static int32_t gHeartRate = 0; //表盘页面的心率数据
#else //Designer仿真
#include <ctime>
#ifndef _MSC_VER
#include <sys/time.h>
#endif /* _MSC_VER*/
//
volatile uint8_t gBacklightVal = 50; //背光值,默认50%
#endif
/**********************************TouchGFX与底层间的访问**********************************/
Model::Model() : modelListener(0)
{
}
//
void Model::tick()
{
static uint8_t tickCount = 0; //减少数据上传的次数,优化界面刷新
tickCount++;
#if defined LINK_HARDWARE
//
if(gSwitchSpace != 0) gSwitchSpace--;
/********************************硬件页面切换*********************************/
//表盘页面
if(gTaskEnMark.UPDATE_DIAL_EN && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->DialPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//应用页面
if(gTaskEnMark.UPDATE_APPPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->AppPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//六轴页面
if(gTaskEnMark.UPDATE_SIX_AXIS_EN && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->SixAxisPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//无线页面
if(gTaskEnMark.UPDATE_WIFIPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->WiFiPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//设置页面
if(gTaskEnMark.UPDATE_SETTINGPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->SettingPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//控制页面
if(gTaskEnMark.UPDATE_MOTORPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->MotorPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//信息页面,温度、湿度与光强度
if(gTaskEnMark.UPDATE_INFOPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->InfoPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//健康页面
if(gTaskEnMark.UPDATE_HEALTHPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->HealthPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//Chip页面
if(gTaskEnMark.UPDATE_CHIPPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->ChipPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//Battery页面
if(gTaskEnMark.UPDATE_BATTERYPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->BatteryPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//Sport页面退出
if(gTaskEnMark.UPDATE_SPORTPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->SportPageExit(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//Alipay页面退出
if(gTaskEnMark.UPDATE_ALIPAYPAGE && (gTaskStateBit.TouchPress == 0) && (!gSwitchSpace))
{
modelListener->AlipayPageChange(gFiveKeyFunc);
gSwitchSpace = 0x0F; //使能切换时间计数
}
//Sport页面进入
if(!HAL_GPIO_ReadPin(USER_KEY_GPIO_Port,USER_KEY_Pin))
{
modelListener->SportPageEnter(3);
}
/********************************更新各类信息*********************************/
//更新时间信息,为使表盘页面滑动操作正常,在屏幕被点按时不更新数据
if(gTaskEnMark.UPDATE_DIAL_EN && (gSystemTime.Seconds != gLastTimeSeconds)&&(gTaskStateBit.TouchPress == 0)) //每秒同步一次界面时间
{
modelListener->updateDate(gSystemDate.Year,gSystemDate.Month,gSystemDate.Date,gSystemDate.WeekDay);
modelListener->updateTime(gSystemTime.Hours, gSystemTime.Minutes, gSystemTime.Seconds);
//更新新值
gLastTimeSeconds = gSystemTime.Seconds;
//更新温度/步数/心率
modelListener->updateTempStepHeart(gTemRH_Val.Tem,gSportStep,gHeartRate);
}
//健康监测信息上传
if(gTaskEnMark.UPDATE_HEALTHPAGE && (gTaskStateBit.TouchPress == 0))
{
//send samples and calculation result to terminal program through UART
if(ch_hr_valid || ch_spo2_valid)
{
modelListener->updateHeartRateInfo(n_heart_rate/4, n_sp02);
gHeartRate = n_heart_rate/4; //保存心率数据值表盘页面
}
//
if(gTaskStateBit.Max30102) //单次测量完成,清除标志
{
ch_hr_valid =0;
ch_spo2_valid=0;
gTaskStateBit.Max30102 = 0;
}
}
//更新欧拉角
if(gTaskEnMark.UPDATE_SIX_AXIS_EN) //六轴界面活动时上传
{
modelListener->updateSixAxis(pitch, roll, yaw);
}
//获取WiFi模组的RSSI值
if((gTaskEnMark.UPDATE_WIFI_RSSI_EN)) //只有在系统主页时,才进行WiFi的RSSI数据读取
{
modelListener->updateWiFiRSSI(gWiFiInfo, ao_wifiRSSI.gRSSI);
}
//更新温湿度信息
if(gTaskEnMark.UPDATE_INFOPAGE) //INFO面活动时上传
{
modelListener->updateInfo(gTemRH_Val.Hum, gTemRH_Val.Tem, gAP3216C_Val.ALS);
}
//更新芯片参数
if(gTaskEnMark.UPDATE_CHIPPAGE && (!(tickCount % 5)))
{
modelListener->updateChipInfor(gChipTempVal, gVrefVal, gVbatVal); //更新芯片温度、参考电压、Vbat
}
//更新电压与电流
if(gTaskEnMark.UPDATE_BATTERYPAGE)
{
modelListener->updateBatteryPageInfo(gCurrentVal, gVoltageVal); //更新电压与电流
}
#else //Designer仿真
timeval timenow;
gettimeofday(&timenow, NULL);
//仿真更新时间
modelListener->updateTime((timenow.tv_sec / 60 / 60) % 24,(timenow.tv_sec
/ 60) % 60,timenow.tv_sec % 60);
#endif
}
//风扇操作
void Model::turnFanStatus(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
HAL_GPIO_WritePin(EXT_FAN_GPIO_Port,EXT_FAN_Pin,GPIO_PIN_SET); //风扇状态的设置
else
HAL_GPIO_WritePin(EXT_FAN_GPIO_Port,EXT_FAN_Pin,GPIO_PIN_RESET);
#endif
}
//振动电机操作
void Model::setMotorStatus(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
HAL_GPIO_WritePin(EXT_MOTOR_GPIO_Port,EXT_MOTOR_Pin,GPIO_PIN_SET); //振动电机状态的设置
else
HAL_GPIO_WritePin(EXT_MOTOR_GPIO_Port,EXT_MOTOR_Pin,GPIO_PIN_RESET);
#endif
}
//排水操作
void Model::drainWaterStatus(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
HAL_GPIO_WritePin(EXT_MOTOR_GPIO_Port,EXT_MOTOR_Pin,GPIO_PIN_SET); //振动电机设置
else
HAL_GPIO_WritePin(EXT_MOTOR_GPIO_Port,EXT_MOTOR_Pin,GPIO_PIN_RESET);
#endif
}
//蜂鸣器操作
void Model::setBuzzerStatus(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
HAL_GPIO_WritePin(RUN_BEEP_GPIO_Port,RUN_BEEP_Pin,GPIO_PIN_SET); //蜂鸣器状态的设置
else
HAL_GPIO_WritePin(RUN_BEEP_GPIO_Port,RUN_BEEP_Pin,GPIO_PIN_RESET);
#endif
}
/*********************gTaskEnMark赋值*************************/
//DialView的任务的状态
void Model::DialPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_DIAL_EN = 1; //任务使能
else
gTaskEnMark.UPDATE_DIAL_EN = 0; //任务清除
#endif
}
//ApplicationPageView的任务的状态
void Model::ApplicationPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_APPPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_APPPAGE = 0; //任务清除
#endif
}
//SixAxisPageView的任务的状态
void Model::SixAxisPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_SIX_AXIS_EN = 1; //任务使能
else
gTaskEnMark.UPDATE_SIX_AXIS_EN = 0; //任务清除
#endif
}
//InfoPageView任务使能
void Model::InfoPageViewTask(bool newStatus)
{
#if defined LINK_HARDWARE
if(newStatus == true)
gTaskEnMark.UPDATE_INFOPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_INFOPAGE = 0; //任务清除
#endif
}
//ChipPageViewTask的任务的状态
void Model::ChipPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_CHIPPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_CHIPPAGE = 0; //任务清除
#endif
}
//设置健康监测任务
void Model::HealthPageViewTask(bool newStatus)
{
#if defined LINK_HARDWARE
if(newStatus == true)
gTaskEnMark.UPDATE_HEALTHPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_HEALTHPAGE = 0; //任务清除
#endif
}
//WiFi连接的任务状态
void Model::WiFiLinkTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_WIFI_RSSI_EN = 1; //任务使能
else
gTaskEnMark.UPDATE_WIFI_RSSI_EN = 0; //任务清除
#endif
}
//WiFi界面的任务状态
void Model::WiFiPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_WIFIPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_WIFIPAGE = 0; //任务清除
#endif
}
//Setting界面的任务状态
void Model::SettingPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_SETTINGPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_SETTINGPAGE = 0; //任务清除
#endif
}
//Motor界面的任务状态
void Model::MotorPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_MOTORPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_MOTORPAGE = 0; //任务清除
#endif
}
//Battery界面的任务状态
void Model::BatteryPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_BATTERYPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_BATTERYPAGE = 0; //任务清除
#endif
}
//运动界面的任务状态
void Model::SportPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_SPORTPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_SPORTPAGE = 0; //任务清除
#endif
}
//Aliplay界面任务状态
void Model::AlipayPageViewTask(bool enable)
{
#if defined LINK_HARDWARE
if(enable == true)
gTaskEnMark.UPDATE_ALIPAYPAGE = 1; //任务使能
else
gTaskEnMark.UPDATE_ALIPAYPAGE = 0; //任务清除
#endif
}
Model.hpp中的函数声明:
#ifndef MODEL_HPP
#define MODEL_HPP
class ModelListener;
class Model
{
public:
Model();
void bind(ModelListener* listener)
{
modelListener = listener;
}
void tick();
//SettingPageView的任务的状态
void SettingPageViewTask(bool enable);
//DialPageView的任务的状态
void DialPageViewTask(bool enable);
//FiveKeyPageView的任务的状态
void FiveKeyPageViewTask(bool enable);
//ApplicationPageView的任务的状态
void ApplicationPageViewTask(bool enable);
//SixAxisPageView的任务的状态
void SixAxisPageViewTask(bool enable);
//InfoPageView的任务的状态
void InfoPageViewTask(bool enable);
//ChipPageView的任务的状态
void ChipPageViewTask(bool enable);
//WiFiPageView的任务的状态
void WiFiPageViewTask(bool enable);
//MotorPageView的任务的状态
void MotorPageViewTask(bool enable);
//BatteryPageView的任务的状态
void BatteryPageViewTask(bool enable);
//SportPageView的任务的状态
void SportPageViewTask(bool enable);
//Aliplay界面任务状态
void AlipayPageViewTask(bool enable);
//控制事件
void turnFanStatus(bool enable);
void setMotorStatus(bool enable);
void setBuzzerStatus(bool enable);
void drainWaterStatus(bool enable);
//HealthPageView的任务的状态
void HealthPageViewTask(bool enable);
//无线连接任务
void WiFiLinkTask(bool enable);
protected:
ModelListener* modelListener;
};
#endif // MODEL_HPP
ModelListener.hpp中的函数声明:
#ifndef MODELLISTENER_HPP
#define MODELLISTENER_HPP
#include <gui/model/Model.hpp>
extern "C" {
#include "stdint.h"
}
class ModelListener
{
public:
ModelListener() : model(0) {}
virtual ~ModelListener() {}
void bind(Model* m)
{
model = m;
}
//更新日期和时间
virtual void updateDate(uint8_t Year, uint8_t Month, uint8_t Date, uint8_t WeekDay) {}
virtual void updateTime(uint8_t Hours, uint8_t Minutes, uint8_t Seconds) {}
//页面跳转
virtual void SettingPageChange(uint8_t newFiveKeyFunc){}
virtual void DialPageChange(uint8_t newFiveKeyFunc){}
virtual void AppPageChange(uint8_t newFiveKeyFunc){}
virtual void SixAxisPageChange(uint8_t newFiveKeyFunc){}
virtual void ChipPageChange(uint8_t newFiveKeyFunc){}
virtual void MotorPageChange(uint8_t newFiveKeyFunc){}
virtual void InfoPageChange(uint8_t newFiveKeyFunc){}
virtual void HealthPageChange(uint8_t newFiveKeyFunc){}
virtual void WiFiPageChange(uint8_t newFiveKeyFunc){}
virtual void BatteryPageChange(uint8_t newFiveKeyFunc){}
virtual void SportPageEnter(uint8_t newKeyFunc){}
virtual void SportPageExit(uint8_t newFiveKeyFunc){}
virtual void AlipayPageChange(uint8_t newFiveKeyFunc){}
//温度/步数/心率上传
virtual void updateTempStepHeart(float newTem, unsigned long newStep, uint32_t newHeartRate){}
//更新姿态信息参数
virtual void updateSixAxis(float pitchVal,float rollVal,float yawVal) {}
//更新芯片内部参数
virtual void updateChipInfor(uint16_t ChipTempVal, uint16_t VrefVal, uint16_t VbatVal) {}
//健康监测信息上传
virtual void updateHeartRateInfo(uint32_t newHeartRate, uint32_t newSPO2) {}
//更新温湿度、光照度数据上传
virtual void updateInfo(float newHum, float newTem, uint16_t newALS) {}
//获取WiFi模组的RSSI值
virtual void updateWiFiRSSI(uint8_t (&pWiFiInfo)[40], uint16_t newRSSI) {}
virtual void updateWalkStep(unsigned long newStep){}
//更新电压、电流
virtual void updateBatteryPageInfo(uint16_t CurrentVal, uint16_t VoltageVal) {}
protected:
Model* model;
};
#endif // MODELLISTENER_HPP