mcu_ab568x/app/projects/AB5681F_240_32M/functions/func_breathe.c
2025-05-30 18:03:10 +08:00

577 lines
23 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "include.h"
#include "func.h"
//#include "func_multiline.h"
#include "app_variable.h"
#if defined(TJD_GUI_Breathe_Show)
#define TRACE_EN 1
#if TRACE_EN
#define TRACE(...) printf(__VA_ARGS__)
#else
#define TRACE(...)
#endif
#define BREATHE_TIME_MIN 1//(1 * 60 * 1000)//(10 * 1000) //时间设置最小值,ms
#define BREATHE_TIME_MAX 3//(30 * 60 * 1000) //时间设置最大值
//速度模式
enum {
BREATHE_MODE_SLOW=1, //缓慢
BREATHE_MODE_MEDIUM, //舒缓
BREATHE_MODE_FAST, //稍快
};
#define ANIMATION_TICK_EXPIRE 20 //动画帧间隔ms
#define BREATHE_ICON_SIZE_MIN 40 //图标缩放最小尺寸
#define BREATHE_ICON_SIZE_MAX 200
#define BREATHE_PREPARE_TIME 3000 //入场动画持续时间
#define BREATHE_FINISH_TIME 500 //出场动画持续时间
#define BREATHE_RHYTHM0 3000 //呼气时间(慢)
#define BREATHE_RHYTHM1 2000 //中
#define BREATHE_RHYTHM2 1500 //快
#define BREATHE_INHALE0 3000 //吸气时间(慢)
#define BREATHE_INHALE1 2000 //中
#define BREATHE_INHALE2 1500 //快
#define BTN_ICON_W 32
#define BTN_ICON_H 32
#define BG_ICON_W 135
#define BG_ICON_H 135
//呼吸检测状态
enum {
BREATHE_STA_IDLE, //空闲
BREATHE_STA_WORKING, //呼吸中
};
//动画状态
enum {
ANIMATION_PREPARE=1, //入场动画
ANIMATION_DELAY, //保持静止
ANIMATION_INHALE, //吸气
// ANIMATION_HOLD, //屏气
ANIMATION_EXHALE, //呼气
ANIMATION_FINISH, //返回idle状态
};
enum {
//按键
COMPO_ID_BTN_START = 1,
COMPO_ID_BTN_MODE,
COMPO_ID_BTN_TIME,
COMPO_ID_BTN_ICON,
//图像
COMPO_ID_PIC_BREATHE,
//文本框
COMPO_ID_TXT_START,
COMPO_ID_TXT_COUNTDUWN,
COMPO_ID_BTN_TIME_ICON,
COMPO_ID_BTN_MODE_ICON,
};
typedef struct f_breathe_t_ {
u8 sta;
u32 tick;
u32 time_past;
u8 animation_sta;
u16 icon_org_size;
u16 icon_size;
s16 icon_deg;
bool motor_flag; //震动标志位
bool heart_flag; //心率测量标志位
bool old_heart_flag; //训练前心率测量标志位
u32 heart_tick; //训练前心率测量开始时间
bool old_heart_tick_flag; //训练前心率测量结束标志位
u32 new_time; //当前状态训练(吸/呼气)时长
bool new_flag; //当前训练状态标志位
} f_breathe_t;
//创建呼吸窗体,创建窗体中不要使用功能结构体 func_cb.f_cb
compo_form_t *func_breathe_form_create(void)
{
//新建窗体和背景
compo_form_t *frm = compo_form_create(true);
//设置标题栏
compo_form_set_mode(frm, COMPO_FORM_MODE_SHOW_TITLE | COMPO_FORM_MODE_SHOW_TIME);
compo_form_set_title(frm, i18n[STR_BREATHE]);
//新建图像
compo_picturebox_t *pic;
pic = compo_picturebox_create(frm, UI_BUF_BREATHE_BREATHE_BIN);
compo_setid(pic, COMPO_ID_PIC_BREATHE);
compo_picturebox_set_pos(pic, GUI_SCREEN_CENTER_X, GUI_SCREEN_CENTER_Y - 21);
//新建按钮
compo_button_t *btn;
btn = compo_button_create_by_image(frm, UI_BUF_BREATHE_BUTTON_START_BIN);
compo_setid(btn, COMPO_ID_BTN_START);
compo_button_set_pos(btn, GUI_SCREEN_CENTER_X, GUI_SCREEN_CENTER_Y + 107);
btn = compo_button_create(frm);
compo_setid(btn, COMPO_ID_BTN_ICON);
compo_button_set_location(btn, GUI_SCREEN_CENTER_X, GUI_SCREEN_CENTER_Y - 21,BG_ICON_W,BG_ICON_H);
btn = compo_button_create(frm);
compo_setid(btn, COMPO_ID_BTN_TIME);
compo_button_set_location(btn, GUI_SCREEN_CENTER_X - 99, GUI_SCREEN_CENTER_Y - 13,BTN_ICON_W,BTN_ICON_H);
compo_picturebox_create_coordinate(frm,UI_BUF_BREATHE_TIME_BIN, GUI_SCREEN_CENTER_X - 99, GUI_SCREEN_CENTER_Y - 13,COMPO_ID_BTN_TIME_ICON,true);
btn = compo_button_create(frm);
compo_setid(btn, COMPO_ID_BTN_MODE);
compo_button_set_location(btn, GUI_SCREEN_CENTER_X + 99, GUI_SCREEN_CENTER_Y - 13,BTN_ICON_W,BTN_ICON_H);
compo_picturebox_create_coordinate(frm,UI_BUF_BREATHE_MODE_BIN, GUI_SCREEN_CENTER_X + 99, GUI_SCREEN_CENTER_Y - 13,COMPO_ID_BTN_MODE_ICON,true);
//创建文本
compo_textbox_t *txt_start = compo_textbox_create(frm, 20);
compo_textbox_set_pos(txt_start, GUI_SCREEN_CENTER_X, GUI_SCREEN_CENTER_Y + 102);
compo_setid(txt_start, COMPO_ID_TXT_START);
compo_textbox_set(txt_start, "");
//倒计时
compo_textbox_t *txt_timedown = compo_textbox_create(frm, 3);
compo_textbox_set_font(txt_timedown, UI_BUF_0FONT_FONT_NUM_24_BIN);
compo_textbox_set_pos(txt_timedown, GUI_SCREEN_CENTER_X, GUI_SCREEN_CENTER_Y + 102);
compo_setid(txt_timedown, COMPO_ID_TXT_COUNTDUWN);
//compo_textbox_set(txt_timedown, "");
return frm;
}
//页面状态转换时组件显示参数初始化
static void func_breathe_disp_init(u8 page_sta)
{
f_breathe_t *f_breathe = (f_breathe_t *)func_cb.f_cb;
compo_button_t *btn = NULL;
compo_picturebox_t *pic_breathe = compo_getobj_byid(COMPO_ID_PIC_BREATHE);
compo_picturebox_t *pic_time = compo_getobj_byid(COMPO_ID_BTN_TIME_ICON);
compo_picturebox_t *pic_mode = compo_getobj_byid(COMPO_ID_BTN_MODE_ICON);
switch (page_sta) {
case BREATHE_STA_IDLE:
btn = compo_getobj_byid(COMPO_ID_BTN_START);
compo_button_set_visible(btn, true);
btn = compo_getobj_byid(COMPO_ID_BTN_ICON);
compo_button_set_visible(btn, true);
btn = compo_getobj_byid(COMPO_ID_BTN_TIME);
compo_button_set_visible(btn, true);
btn = compo_getobj_byid(COMPO_ID_BTN_MODE);
compo_button_set_visible(btn, true);
compo_picturebox_set_visible(pic_mode,true);
compo_picturebox_set_visible(pic_time,true);
f_breathe->sta = BREATHE_STA_IDLE;
break;
case BREATHE_STA_WORKING:
btn = compo_getobj_byid(COMPO_ID_BTN_START);
compo_button_set_visible(btn, false);
btn = compo_getobj_byid(COMPO_ID_BTN_ICON);
compo_button_set_visible(btn, true);
btn = compo_getobj_byid(COMPO_ID_BTN_TIME);
compo_button_set_visible(btn, false);
btn = compo_getobj_byid(COMPO_ID_BTN_MODE);
compo_button_set_visible(btn, false);
compo_picturebox_set_visible(pic_mode,false);
compo_picturebox_set_visible(pic_time,false);
f_breathe->sta = BREATHE_STA_IDLE;
f_breathe->animation_sta = ANIMATION_PREPARE;
f_breathe->time_past = 0;
f_breathe->icon_deg = 0;
f_breathe->icon_size = f_breathe->icon_org_size;
f_breathe->tick = tick_get();
break;
default:
break;
}
compo_picturebox_set_size(pic_breathe, f_breathe->icon_org_size, f_breathe->icon_org_size);
compo_picturebox_set_rotation(pic_breathe, 0);
}
//呼吸功能事件处理
static void func_breathe_process(void)
{
static u16 frames, rhythm, size_step_time, finish_deg, rhythm1;
static s16 deg_step, size_step;
s16 temp;
char text_buf[3] = {0};
u32 new_tick;
f_breathe_t *f_breathe = (f_breathe_t *)func_cb.f_cb;
compo_picturebox_t *pic_breathe = compo_getobj_byid(COMPO_ID_PIC_BREATHE);
compo_textbox_t *txt = compo_getobj_byid(COMPO_ID_TXT_START);
compo_textbox_t *txt_countdown = compo_getobj_byid(COMPO_ID_TXT_COUNTDUWN);
if (f_breathe->sta == BREATHE_STA_WORKING) {
reset_sleep_delay_all();
}
switch (f_breathe->sta) {
case BREATHE_STA_IDLE:
break;
case BREATHE_STA_WORKING:
if(f_breathe->old_heart_flag==true)
{
f_breathe->old_heart_flag=false;
bsp_sensor_hr_start(MeasureMode_Heart);
f_breathe->heart_tick=tick_get();
}
new_tick=tick_get();
if(new_tick-f_breathe->heart_tick>=15100&&f_breathe->old_heart_tick_flag==true)
{
f_breathe->old_heart_tick_flag=false;
SysVariable.breath_heart[0]=SysVariable.heartRateModule.heart;
bsp_sensor_hr_stop();
}
if (tick_check_expire(f_breathe->tick, ANIMATION_TICK_EXPIRE)) {
f_breathe->tick = tick_get();
f_breathe->time_past += ANIMATION_TICK_EXPIRE;
f_breathe->new_time += ANIMATION_TICK_EXPIRE;
switch (f_breathe->animation_sta) {
case ANIMATION_PREPARE:
if (f_breathe->time_past <= ANIMATION_TICK_EXPIRE) {
TRACE("BREATHE PREPARE\n");
frames = (BREATHE_PREPARE_TIME / ANIMATION_TICK_EXPIRE);
deg_step = (-3600 - frames / 2) / frames; //转1圈
temp = (BREATHE_ICON_SIZE_MAX - f_breathe->icon_size) + (BREATHE_ICON_SIZE_MAX - BREATHE_ICON_SIZE_MIN);
size_step = (temp + frames / 2) / frames;
if (size_step == 0) {
size_step_time = ANIMATION_TICK_EXPIRE * ((frames + temp / 2) / temp);
size_step = 1;
} else {
size_step_time = ANIMATION_TICK_EXPIRE;
}
}
f_breathe->icon_deg += deg_step;
if (f_breathe->time_past % size_step_time == 0) {
f_breathe->icon_size += size_step*2; //先放大后缩小
if (f_breathe->icon_size >= BREATHE_ICON_SIZE_MAX) {
size_step = -size_step;
} else if (f_breathe->icon_size <= BREATHE_ICON_SIZE_MIN) {
size_step = 0;
}
}
sprintf(text_buf, "%dS", (frames*ANIMATION_TICK_EXPIRE-1)/1000+1);
compo_textbox_set(txt_countdown,text_buf);
if (--frames == 0) {
f_breathe->icon_deg = 0;
f_breathe->icon_size = BREATHE_ICON_SIZE_MIN;
f_breathe->animation_sta = ANIMATION_INHALE;
f_breathe->time_past = 0;
f_breathe->motor_flag=true;
f_breathe->new_time=0;
f_breathe->new_flag=true;
compo_textbox_set(txt_countdown,"");
f_breathe->heart_flag=true;
}
// TRACE("%d deg[%d] size[%d]\n", frames, f_breathe->icon_deg, f_breathe->icon_size);
break;
/*
case ANIMATION_DELAY:
if (f_breathe->time_past <= ANIMATION_TICK_EXPIRE) {
TRACE("BREATHE DELAY\n");
frames = (BREATHE_PREPARE_TIME / ANIMATION_TICK_EXPIRE); //入场动画一半运动一半静止
}
sprintf(text_buf, "%dS", (frames*ANIMATION_TICK_EXPIRE-1)/1000+1);
compo_textbox_set(txt_countdown,text_buf);
if (--frames == 0) {
f_breathe->motor_flag=true;
f_breathe->animation_sta = ANIMATION_INHALE;
f_breathe->time_past = 0;
f_breathe->new_time=0;
f_breathe->new_flag=true;
compo_textbox_set(txt_countdown,"");
f_breathe->heart_flag=true;
}
break;
*/
case ANIMATION_INHALE:
// case ANIMATION_EXHALE:
if (f_breathe->time_past <= ANIMATION_TICK_EXPIRE) {
TRACE("BREATHE START\n");
compo_textbox_set(txt, i18n[STR_BREATHE_INHALE]);
rhythm = sys_cb.breathe_mode == BREATHE_MODE_SLOW ? BREATHE_RHYTHM0 : \
(sys_cb.breathe_mode == BREATHE_MODE_FAST ? BREATHE_RHYTHM2 : BREATHE_RHYTHM1);
rhythm1 = sys_cb.breathe_mode == BREATHE_MODE_SLOW ? BREATHE_INHALE0 : \
(sys_cb.breathe_mode == BREATHE_MODE_FAST ? BREATHE_INHALE2 : BREATHE_INHALE1);
if(f_breathe->motor_flag==true)
{
f_breathe->motor_flag=false;
sys_cb.motor_flag = 1;
set_func_motor(80,5,5,rhythm1/1000);
}
frames = (rhythm / ANIMATION_TICK_EXPIRE);
deg_step = 1800 / frames; //呼吸各半圈
temp = BREATHE_ICON_SIZE_MAX - BREATHE_ICON_SIZE_MIN;
size_step = (temp + frames / 2) / frames;
if (size_step == 0) {
size_step_time = ANIMATION_TICK_EXPIRE * ((frames + temp / 2) / temp);
size_step = 1;
} else {
size_step_time = ANIMATION_TICK_EXPIRE;
}
// TRACE("rhythm[%d] frames[%d] degstep[%d] sizestep[%d]\n", rhythm, frames, deg_step, size_step);
}
if (f_breathe->time_past % size_step_time == 0) {
if (size_step > 0) {
if (f_breathe->icon_size <= BREATHE_ICON_SIZE_MAX - size_step) {
f_breathe->icon_size += size_step;
// f_breathe->icon_deg = (f_breathe->icon_deg + deg_step) % 3600; //缩放旋转一起停
} else {
f_breathe->icon_size = BREATHE_ICON_SIZE_MAX;
}
} else {// 呼气缩小
if (f_breathe->icon_size >= BREATHE_ICON_SIZE_MIN - size_step) {
f_breathe->icon_size += size_step;
// f_breathe->icon_deg = (f_breathe->icon_deg + deg_step) % 3600;
} else {
f_breathe->icon_size = BREATHE_ICON_SIZE_MIN;
}
}
}
f_breathe->icon_deg = (f_breathe->icon_deg + deg_step) % 3600; //缩放停止后继续旋转
//当前为训练状态为呼气
if (f_breathe->new_time == rhythm1 && f_breathe->new_flag==true) {
f_breathe->new_flag=false;
f_breathe->new_time=0;
size_step = -size_step;
if (abs_s(f_breathe->icon_size - BREATHE_ICON_SIZE_MAX) < abs_s(f_breathe->icon_size - BREATHE_ICON_SIZE_MIN)) {
compo_textbox_set(txt, i18n[STR_BREATHE_EXHALE]); //呼气
bsp_motor_stop(MOTOR_PORT);
f_breathe->motor_flag=true;
}
TRACE("total:%ds past:%ds\n", sys_cb.breathe_duration, f_breathe->time_past / 1000);
}
else if(f_breathe->new_time == rhythm && f_breathe->new_flag==false)
{
size_step = -size_step;
f_breathe->new_flag=true;
f_breathe->new_time=0;
if(f_breathe->motor_flag==true)
{
f_breathe->motor_flag=false;
sys_cb.motor_flag = 1;
set_func_motor(80,5,5,rhythm/1000);
}
compo_textbox_set(txt, i18n[STR_BREATHE_INHALE]); //吸气
}
//最后十秒测试呼吸训练之后的心率
if(sys_cb.breathe_duration*60*1000-f_breathe->time_past<=15100)
{
if(f_breathe->heart_flag==true)
{
f_breathe->heart_flag=false;
bsp_sensor_hr_start(MeasureMode_Heart);
}
}
if (f_breathe->time_past >= sys_cb.breathe_duration*60*1000) {
f_breathe->time_past = 0;
f_breathe->animation_sta = ANIMATION_FINISH;
SysVariable.breath_heart[1]=SysVariable.heartRateModule.heart;
printf("1111SysVariable.breath_heart[1]----->%d\n",SysVariable.breath_heart[1]);
bsp_sensor_hr_stop();
bsp_motor_stop(MOTOR_PORT);
}
break;
case ANIMATION_FINISH:
if (f_breathe->time_past <= ANIMATION_TICK_EXPIRE) {
TRACE("BREATHE FINISH\n");
compo_textbox_set(txt, "");
frames = BREATHE_FINISH_TIME / ANIMATION_TICK_EXPIRE;
finish_deg = abs_s(0 - f_breathe->icon_deg) <= abs_s(3600 - f_breathe->icon_deg) ? 0 : 3600;
temp = finish_deg - f_breathe->icon_deg;
deg_step = (temp + (temp < 0 ? -(frames / 2) : (frames / 2))) / frames;
if (deg_step == 0 && temp != 0) {
deg_step = temp < 0 ? -1 : 1;
}
temp = f_breathe->icon_org_size - f_breathe->icon_size;
size_step = (temp + (temp < 0 ? -(frames / 2) : (frames / 2))) / frames;
if (size_step == 0 && temp != 0) {
size_step = temp < 0 ? -1 : 1;
}
}
// if (f_breathe->time_past % 100 == 0) {
// TRACE("deg[%d] size[%d] degstep[%d] sizestep[%d]\n", f_breathe->icon_deg, f_breathe->icon_size, deg_step, size_step);
// }
if (f_breathe->icon_deg != finish_deg || f_breathe->icon_size != f_breathe->icon_org_size) {
if (abs_s(finish_deg - f_breathe->icon_deg) <= abs_s(deg_step)) {
f_breathe->icon_deg = finish_deg;
} else {
f_breathe->icon_deg += deg_step;
}
if (abs_s(f_breathe->icon_org_size - f_breathe->icon_size) <= abs_s(size_step)) {
f_breathe->icon_size = f_breathe->icon_org_size;
} else {
f_breathe->icon_size += size_step;
}
if (f_breathe->time_past > BREATHE_FINISH_TIME * 2) { //意外超时直接退出
f_breathe->icon_deg = finish_deg;
f_breathe->icon_size = f_breathe->icon_org_size;
}
} else {
func_breathe_disp_init(BREATHE_STA_IDLE);
f_breathe->sta = BREATHE_STA_IDLE;
}
//正常做完呼吸训练得到呼吸界面之后的心率值才会进去呼吸结束界面
if(SysVariable.breath_heart[1]!=0)
{
func_cb.sta = FUNC_BREATHE_END;
}
break;
default:
break;
}
compo_picturebox_set_size(pic_breathe, f_breathe->icon_size, f_breathe->icon_size);
compo_picturebox_set_rotation(pic_breathe, f_breathe->icon_deg);
}
break;
default:
break;
}
if(f_breathe->sta!=ANIMATION_PREPARE)
{
compo_textbox_set(txt_countdown,"");
}
func_process();
}
//单击按钮
static void func_breathe_button_click(void)
{
f_breathe_t *f_breathe = (f_breathe_t *)func_cb.f_cb;
int id = compo_get_button_id();
switch(id) {
case COMPO_ID_BTN_START:
case COMPO_ID_BTN_ICON:
SysVariable.breath_heart[1] = 0;
SysVariable.breath_heart[0] = 0;
f_breathe->old_heart_tick_flag = true;
f_breathe->old_heart_flag = true;
if (f_breathe->sta == BREATHE_STA_IDLE) {
func_breathe_disp_init(BREATHE_STA_WORKING);
f_breathe->sta = BREATHE_STA_WORKING;
}
break;
case COMPO_ID_BTN_TIME:
//sys_cb.multiline_id = BREATHE_TIME_MULTILINE_SET;
func_cb.sta = FUNC_MULTILINE;
printf(">>>breathe_duration:%ds\n", sys_cb.breathe_duration);
break;
case COMPO_ID_BTN_MODE:
//sys_cb.multiline_id = BREATHE_MODE_MULTILINE_SET;
func_cb.sta = FUNC_BREATHE_MODE;
printf(">>>breathe_mode:%d\n", sys_cb.breathe_mode);
break;
default:
break;
}
}
//呼吸功能消息处理
static void func_breathe_message(size_msg_t msg)
{
f_breathe_t *f_breathe = (f_breathe_t *)func_cb.f_cb;
if (f_breathe->sta == BREATHE_STA_IDLE) {
switch (msg) {
case MSG_CTP_CLICK:
func_breathe_button_click();
break;
case MSG_CTP_SHORT_UP:
case MSG_CTP_SHORT_DOWN:
case MSG_CTP_SHORT_LEFT:
case MSG_CTP_LONG:
if (func_cb.flag_sort) {
func_message(msg);
}
break;
case MSG_CTP_SHORT_RIGHT:
func_message(msg);
break;
case MSG_QDEC_FORWARD:
case MSG_QDEC_BACKWARD:
break;
default:
func_message(msg);
break;
}
} else {
switch (msg) {
case KU_BACK:
case MSG_CTP_SHORT_RIGHT:
f_breathe->time_past = 0;
f_breathe->old_heart_tick_flag=false;
f_breathe->old_heart_flag=false;
f_breathe->animation_sta = ANIMATION_FINISH;
f_breathe->motor_flag=false;
bsp_sensor_hr_stop();
bsp_motor_stop(MOTOR_PORT);
break;
default:
func_message(msg);
break;
}
}
}
//进入呼吸功能
static void func_breathe_enter(void)
{
func_cb.f_cb = func_zalloc(sizeof(f_breathe_t));
func_cb.frm_main = func_breathe_form_create();
f_breathe_t *f_breathe = (f_breathe_t *)func_cb.f_cb;
compo_picturebox_t *pic_breathe = compo_getobj_byid(COMPO_ID_PIC_BREATHE);
f_breathe->icon_org_size = compo_picturebox_get_location(pic_breathe).wid;
//f_breathe->old_heart_tick_flag=true;
//f_breathe->old_heart_flag=true;
sys_cb.breathe_mode = MAX(BREATHE_MODE_SLOW,MIN(sys_cb.breathe_mode, BREATHE_MODE_FAST));
sys_cb.breathe_duration = MAX(BREATHE_TIME_MIN, MIN(BREATHE_TIME_MAX, sys_cb.breathe_duration));
}
//退出呼吸功能
static void func_breathe_exit(void)
{
f_breathe_t *f_breathe = (f_breathe_t *)func_cb.f_cb;
f_breathe->old_heart_tick_flag=false;
f_breathe->old_heart_flag=false;
bsp_sensor_hr_stop();
bsp_motor_stop(MOTOR_PORT);
func_cb.last = FUNC_BREATHE;
}
//呼吸功能
void func_breathe(void)
{
printf("%s\n", __func__);
func_breathe_enter();
while (func_cb.sta == FUNC_BREATHE) {
func_breathe_process();
func_breathe_message(msg_dequeue());
}
func_breathe_exit();
}
#endif