From 7425e315cbf0602dcba0d1e3767497d45bb1d0c8 Mon Sep 17 00:00:00 2001 From: zhaohe Date: Sun, 28 Jan 2024 19:53:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=85=85=E7=94=B5=E5=8A=A8?= =?UTF-8?q?=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/basic/ssd1306/driver_ssd1306_basic.c | 14 ++- app/src/one_conduction/display_manager.c | 130 +++++++++++++-------- app/src/one_conduction/display_manager.h | 5 +- app/src/one_conduction/one_conduction_board.c | 20 ++++ app/src/one_conduction/one_conduction_board.h | 9 +- app/src/one_conduction/one_conduction_main.c | 162 +++++++++++++++++++++++--- 6 files changed, 267 insertions(+), 73 deletions(-) diff --git a/app/src/basic/ssd1306/driver_ssd1306_basic.c b/app/src/basic/ssd1306/driver_ssd1306_basic.c index 4121ae6..d7ee073 100644 --- a/app/src/basic/ssd1306/driver_ssd1306_basic.c +++ b/app/src/basic/ssd1306/driver_ssd1306_basic.c @@ -519,7 +519,7 @@ uint8_t ssd1306_basic_draw_screen(const char *img) { for (size_t i = 0; i < 1024; i++) { gs_handle.gram[i % 128][i / 128] = img[i]; } - return 0; + return 0; } uint8_t ssd1306_basic_gram_update() { return ssd1306_gram_update(&gs_handle); } #include "znordic.h" @@ -586,6 +586,10 @@ uint8_t ssd1306_basic_draw_battery_level(uint8_t xs, uint8_t ys, uint8_t *xchang * ******** */ + if (level >= 100) { + level = 100; + } + uint8_t xoff = 0; uint8_t yoff = 0; @@ -666,6 +670,8 @@ uint8_t ssd1306_basic_draw_battery_level(uint8_t xs, uint8_t ys, uint8_t *xchang uint16_t batterylevel = (100 - level) / 100.0 * batteryInterBodyHigh; if (yoff >= batterylevel) { ssd1306_gram_write_point(&gs_handle, xs + xoff, ys + yoff, 1); + } else { + ssd1306_gram_write_point(&gs_handle, xs + xoff, ys + yoff, 0); } } else if (xoff >= batteryInterBodyWidth + border_width * 1 + blank_width * 2) { @@ -716,7 +722,7 @@ uint8_t ssd1306_basic_draw_progress(uint8_t xs, uint8_t ys, uint16_t width, uint */ /** - * @brief + * @brief */ for (uint16_t i = 0; i < width; i++) { ssd1306_gram_write_point(&gs_handle, xs + i, ys, 1); @@ -724,7 +730,7 @@ uint8_t ssd1306_basic_draw_progress(uint8_t xs, uint8_t ys, uint16_t width, uint ys += 1; /** - * @brief + * @brief */ uint16_t progress_width = width * progress / 100.0; uint16_t markpos_off = width * markpos / 100.0; @@ -744,7 +750,7 @@ uint8_t ssd1306_basic_draw_progress(uint8_t xs, uint8_t ys, uint16_t width, uint ys += high - 2; /** - * @brief + * @brief */ for (uint16_t i = 0; i < width; i++) { ssd1306_gram_write_point(&gs_handle, xs + i, ys, 1); diff --git a/app/src/one_conduction/display_manager.c b/app/src/one_conduction/display_manager.c index d398539..ac1d8ca 100644 --- a/app/src/one_conduction/display_manager.c +++ b/app/src/one_conduction/display_manager.c @@ -11,19 +11,6 @@ #include "one_conduction_board.h" PageState_t g_pageState; -void dsp_mgr_init(void) { - FontLibrary_regsiter(&fontclocklib); - FontLibrary_regsiter(&font8x8_xo_lib); - - // fronlib16.asciifront = assci16code; - // fronlib16.asciifrontIndex = "0123456789Ero."; - // fronlib16.asciifrontIndexLen = strlen(fronlib16.asciifrontIndex); - // fronlib16.gbkfront = gbk16code; - // fronlib16.gbkfrontIndex = "米开机中测距录像错误电量充"; - // fronlib16.gbkfrontIndexLen = strlen(fronlib16.gbkfrontIndex); - // fronlib16.eachfrontlen = 32; -} - void dsp_mgr_change_to_page(page_t page) { // g_pageState.page = page; g_pageState.last_page = page; @@ -39,18 +26,7 @@ uint32_t dsp_mgr_get_page_elapsed_time_s(void) { // void dsp_mgr_schedule(void) {} -PageState_t* dsp_mgr_get_state(void) { return NULL;} - -void dsp_mgr_change_to_poweroff() { - /** - * @brief - * 1. 关闭屏幕,关闭屏幕电源 - * 2. 卸载屏幕外设 - */ - SingleLeadECG_screen_deinit(); -} - -void dsp_mgr_poweron() { SingleLeadECG_screen_init(); } +PageState_t* dsp_mgr_get_state(void) { return NULL; } void dsp_mgr_change_to_welcome() { /** @@ -66,7 +42,6 @@ void dsp_mgr_change_to_welcome() { dsp_mgr_change_to_page(kPage_welcome); } - int compute_x_pos_by_center(int16_t x, int16_t width) { return x - width / 2; } int compute_y_pos_by_center(int16_t y, int16_t height) { return y - height / 2; } @@ -87,22 +62,6 @@ void dsp_mgr_change_to_main() { dsp_mgr_change_to_page(kPage_main); } -void dsp_mgr_change_to_chargingPage() { - int16_t batterylevel = 80; - int16_t hour = 12; - int16_t min = 32; - - ssd1306_basic_clear(); - uint8_t x = 20; - uint8_t y = 32 - fontclocklib.heightPixel / 2; // 12*4 +12 - uint8_t xchange, ychange; - - ssd1306_basic_draw_str(x, y, &xchange, &ychange, fmt("%02d:%02d", hour, min), &fontclocklib); - x = x + 2 + xchange; - ssd1306_basic_draw_battery_level(x, y, &xchange, &ychange, batterylevel, 12, 22); - ssd1306_basic_gram_update(); - dsp_mgr_change_to_page(kPage_main); -} void dsp_mgr_change_to_preparePage() { ssd1306_basic_clear(); uint8_t x = 0; @@ -120,19 +79,16 @@ void dsp_mgr_change_to_preparePage() { static wave_drawer_t wave_drawer; void dsp_mgr_change_to_sampling() { // - - + wave_drawer_init(&wave_drawer, 1, 1, 120, 40); wave_drawer_draw_border(&wave_drawer); - uint8_t xchange, ychange; ssd1306_basic_draw_progress(1, 47, 80, 8, 50, 30); ssd1306_basic_draw_str(82, 43, &xchange, &ychange, "<>123", &font_asicc16x8_lib); ssd1306_basic_gram_update(); - } void dsp_mgr_draw_point(int16_t y) { @@ -196,3 +152,85 @@ void dsp_mgr_change_to_storagingSuc() { ssd1306_basic_gram_update(); dsp_mgr_change_to_page(kPage_storagingSuc); } + +#define SCREEN_CENTER_X 64 +#define SCREEN_CENTER_Y 32 + +#define CHARGE_BATTERY_WIDTH (12 * 1.2) +#define CHARGE_BATTERY_HEIGHT (22 * 1.2) + +/******************************************************************************* + * 充电页面 * + *******************************************************************************/ +/** + * 显示元素: + * 1.电池电量 + * --------------- + * | _ | + * | | | | + * | | + * --------------- + * + * 动画效果: + * 1. 电池电量变化 + * + */ +static uint32_t m_nowshowbatterylevel; // 当前显示的电池电量 + +void dsp_mgr_change_to_chargingPage() { + ssd1306_basic_clear(); + uint8_t x = compute_x_pos_by_center(SCREEN_CENTER_X, CHARGE_BATTERY_WIDTH); + uint8_t y = compute_y_pos_by_center(SCREEN_CENTER_Y, CHARGE_BATTERY_HEIGHT); + uint8_t xchange, ychange; + ssd1306_basic_draw_battery_level(x, y, &xchange, &ychange, SingleLeadECG_battery_val(), CHARGE_BATTERY_WIDTH, CHARGE_BATTERY_HEIGHT); + m_nowshowbatterylevel = SingleLeadECG_battery_val(); + ssd1306_basic_gram_update(); + dsp_mgr_change_to_page(kPage_chargingPage); +} + +void chargingPage_set_batteryLevel(int batteryLevel) { + uint8_t x = compute_x_pos_by_center(SCREEN_CENTER_X, CHARGE_BATTERY_WIDTH); + uint8_t y = compute_y_pos_by_center(SCREEN_CENTER_Y, CHARGE_BATTERY_HEIGHT); + uint8_t xchange, ychange; + ssd1306_basic_draw_battery_level(x, y, &xchange, &ychange, batteryLevel, CHARGE_BATTERY_WIDTH, CHARGE_BATTERY_HEIGHT); + ssd1306_basic_gram_update(); +} + +void chargingPage_schedule() { // + static uint32_t last_update_time = 0; + if (znordic_haspassed_ms(last_update_time) < 300) { + return; + } + + last_update_time = znordic_getpower_on_ms(); + m_nowshowbatterylevel += 5; + if (m_nowshowbatterylevel > 100) { + m_nowshowbatterylevel = SingleLeadECG_battery_val(); + } + chargingPage_set_batteryLevel(m_nowshowbatterylevel); +} + +/******************************************************************************* + * SCHEDULE * + *******************************************************************************/ + +APP_TIMER_DEF(m_dsp_mgr_schedule_tmr); +static void dsp_mgr_schedule_tmr_cb(void* p_context) { // + if (g_pageState.page == kPage_chargingPage) { + chargingPage_schedule(); + } +} + +void dsp_mgr_init(void) { + static bool timer_inited = false; + SingleLeadECG_screen_init(); + if (!timer_inited) { + ZERROR_CHECK(app_timer_create(&m_dsp_mgr_schedule_tmr, APP_TIMER_MODE_REPEATED, dsp_mgr_schedule_tmr_cb)); + } + ZERROR_CHECK(app_timer_start(m_dsp_mgr_schedule_tmr, APP_TIMER_TICKS(100), NULL)); + timer_inited = true; +} +void dsp_mgr_uninit(void) { + app_timer_stop(m_dsp_mgr_schedule_tmr); + SingleLeadECG_screen_deinit(); +} diff --git a/app/src/one_conduction/display_manager.h b/app/src/one_conduction/display_manager.h index cc1f630..06426e4 100644 --- a/app/src/one_conduction/display_manager.h +++ b/app/src/one_conduction/display_manager.h @@ -56,14 +56,13 @@ typedef struct { } PageState_t; void dsp_mgr_init(void); +void dsp_mgr_uninit(void); void dsp_mgr_schedule(void); + PageState_t* dsp_mgr_get_state(void); uint32_t dsp_mgr_get_page_elapsed_time_s(void); -void dsp_mgr_poweron(); -void dsp_mgr_poweroff(); - void dsp_mgr_draw_point(int16_t y); void dsp_mgr_change_to_poweroff(); diff --git a/app/src/one_conduction/one_conduction_board.c b/app/src/one_conduction/one_conduction_board.c index 7416c42..84bbb40 100644 --- a/app/src/one_conduction/one_conduction_board.c +++ b/app/src/one_conduction/one_conduction_board.c @@ -58,6 +58,7 @@ void SingleLeadECG_adc_module_init() { adccfg.resolution = NRF_SAADC_RESOLUTION_12BIT; // 4096 等于满采样率 ZERROR_CHECK(nrf_drv_saadc_init(&adccfg, NULL)); } +void SingleLeadECG_adc_module_deinit() { nrf_drv_saadc_uninit(); } /******************************************************************************* * 蜂鸣器 * @@ -80,6 +81,8 @@ static nrf_drv_pwm_config_t const m_beep_pwm0_config0 = { .step_mode = NRF_PWM_STEP_AUTO, }; void SingleLeadECG_beep_init() { APP_ERROR_CHECK(nrfx_pwm_init(&m_beep_pwm0, &m_beep_pwm0_config0, NULL)); } +void SingleLeadECG_beep_deinit() { nrfx_pwm_uninit(&m_beep_pwm0); } + void SingleLeadECG_beep_set_state(bool state) { if (state) { m_beep_pwm0_seq_values.channel_0 = 23; // 设置占空比,数值最大不超过 top_value @@ -151,7 +154,12 @@ uint8_t SingleLeadECG_reset_gpio_write(uint8_t value) { /******************************************************************************* * LED * *******************************************************************************/ +static bool io_init = false; + void SingleLeadECG_led_init() { + if (io_init) { + return; + } znrf_gpio_cfg_output(LED_GREEN_PIN, NRF_GPIO_PIN_NOPULL); znrf_gpio_cfg_output(LED_BLUE_PIN, NRF_GPIO_PIN_NOPULL); @@ -160,7 +168,13 @@ void SingleLeadECG_led_init() { SingleLeadECG_led_green_set_state(false); SingleLeadECG_led_blue_set_state(false); + io_init = true; +} +void SingleLeadECG_led_deinit() { + SingleLeadECG_led_green_set_state(false); + SingleLeadECG_led_blue_set_state(false); } + void SingleLeadECG_led_green_set_state(bool state) { if (state) { nrf_gpio_pin_set(LED_GREEN_PIN); @@ -191,6 +205,8 @@ void SingleLeadECG_ecg_init() { ZERROR_CHECK(nrfx_saadc_channel_init(ECG_ADC_CHANNEL, &channel_config)); } +void SingleLeadECG_ecg_deinit() { nrfx_saadc_channel_uninit(ECG_ADC_CHANNEL); } + void SingleLeadECG_ecg_io_init() { nrf_gpio_cfg_sense_input(ECG_NLOD_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_NOSENSE); nrf_gpio_cfg_sense_input(ECG_PLOD_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_NOSENSE); @@ -208,11 +224,15 @@ void SingleLeadECG_battery_init() { channel_config.acq_time = NRF_SAADC_ACQTIME_10US; ZERROR_CHECK(nrfx_saadc_channel_init(BATTERY_ADC_CHANNEL, &channel_config)); } +void SingleLeadECG_battery_deinit() { nrfx_saadc_channel_uninit(BATTERY_ADC_CHANNEL); } + int16_t SingleLeadECG_battery_get_adc_val() { int16_t val = znrf_adc_channel_read_val(BATTERY_ADC_CHANNEL); return val; } +int16_t SingleLeadECG_battery_val() { return 50; } + void SingleLeadECG_battery_charge_detect_io_init() { // nrf_gpio_cfg_sense_input(BATTERY_CHARGE_DETECT_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_NOSENSE); } diff --git a/app/src/one_conduction/one_conduction_board.h b/app/src/one_conduction/one_conduction_board.h index 23fed79..32623d8 100644 --- a/app/src/one_conduction/one_conduction_board.h +++ b/app/src/one_conduction/one_conduction_board.h @@ -15,11 +15,13 @@ * basic * *******************************************************************************/ void SingleLeadECG_adc_module_init(); +void SingleLeadECG_adc_module_deinit(); /******************************************************************************* * 蜂鸣器 * *******************************************************************************/ void SingleLeadECG_beep_init(); +void SingleLeadECG_beep_deinit(); void SingleLeadECG_beep_set_state(bool state); /******************************************************************************* @@ -47,6 +49,7 @@ void SingleLeadECG_screen_deinit(); * LED * *******************************************************************************/ void SingleLeadECG_led_init(); +void SingleLeadECG_led_deinit(); void SingleLeadECG_led_green_set_state(bool state); void SingleLeadECG_led_blue_set_state(bool state); @@ -55,7 +58,7 @@ void SingleLeadECG_led_blue_set_state(bool state); *******************************************************************************/ void SingleLeadECG_ecg_init(); - +void SingleLeadECG_ecg_deinit(); void SingleLeadECG_ecg_io_init(); uint32_t SingleLeadECG_ecg_nlod_get_connected_state(); @@ -66,8 +69,10 @@ int16_t SingleLeadECG_ecg_plod_get_ecg_val(); * BATTERY * *******************************************************************************/ void SingleLeadECG_battery_init(); +void SingleLeadECG_battery_deinit(); int16_t SingleLeadECG_battery_get_adc_val(); +int16_t SingleLeadECG_battery_val(); + void SingleLeadECG_battery_charge_detect_io_init(); bool SingleLeadECG_battery_charge_get_state(); - diff --git a/app/src/one_conduction/one_conduction_main.c b/app/src/one_conduction/one_conduction_main.c index 65d7a81..96ea901 100644 --- a/app/src/one_conduction/one_conduction_main.c +++ b/app/src/one_conduction/one_conduction_main.c @@ -7,17 +7,6 @@ #include "sample_data_manager.h" #include "znordic.h" -/******************************************************************************* - * GLOBAL * - *******************************************************************************/ - -ZDATACHANNEL_DEF(m_zhrs, 2 /*优先级*/, 1 /*client num*/); // 蓝牙服务 -APP_TIMER_DEF(m_state_machine_driver_tmr); // 状态机驱动定时器 -APP_TIMER_DEF(m_plod_state_event_detect_tmr); // 导联连接状态检测定时器 -APP_TIMER_DEF(m_charge_event_detect_tmr); // 充电事件检测 -extern uint32_t g_nrf_log_tx_pin; -#define SCHED_MAX_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */ - typedef enum { kplod_connected_event = 0, // 导联连接事件 kplod_disconnected_event, // 导联断开事件 @@ -26,8 +15,59 @@ typedef enum { kplod_start_charge_event, // 充电事件 kplod_end_charge_event, // 充电结束事件 + kevent_tmr_scheduler_event, // 定时器调度事件 } app_event_type_t; +typedef enum { + // 待机 + kdevice_state_standby = 0, + // 开机 + kdevice_state_poweron, + // 首页 + kdevice_state_home, + // 提示用户保持静止 + kdevice_state_keep_still, + // 采集中 + kdevice_state_sampling, + // 采集正常结束 + kdevice_state_sampling_end, + // 存储中 + kdevice_state_storing, + // 采集完成 + kdevice_state_sampling_complete, + // 采集异常 + kdevice_state_sampling_error, + // 充电中 + kdevice_state_charging, +} device_state_t; + +static const char* device_state_to_str(device_state_t ds) { + switch (ds) { + case kdevice_state_standby: + return "standby"; + case kdevice_state_poweron: + return "poweron"; + case kdevice_state_home: + return "home"; + case kdevice_state_keep_still: + return "keep_still"; + case kdevice_state_sampling: + return "sampling"; + case kdevice_state_sampling_end: + return "sampling_end"; + case kdevice_state_storing: + return "storing"; + case kdevice_state_sampling_complete: + return "sampling_complete"; + case kdevice_state_sampling_error: + return "sampling_error"; + case kdevice_state_charging: + return "charging"; + default: + return "unknow"; + } +} + typedef struct { app_event_type_t eventType; union { @@ -36,6 +76,20 @@ typedef struct { } app_event_t; /******************************************************************************* + * GLOBAL * + *******************************************************************************/ + +ZDATACHANNEL_DEF(m_zhrs, 2 /*优先级*/, 1 /*client num*/); // 蓝牙服务 +APP_TIMER_DEF(m_state_machine_driver_tmr); // 状态机驱动定时器 +APP_TIMER_DEF(m_plod_state_event_detect_tmr); // 导联连接状态检测定时器 +APP_TIMER_DEF(m_charge_event_detect_tmr); // 充电事件检测 +extern uint32_t g_nrf_log_tx_pin; +static device_state_t m_device_state = kdevice_state_standby; // 设备状态 +static uint32_t m_change_to_cur_state_tp = 0; // 切换到当前状态的时间戳 + +#define SCHED_MAX_EVENT_DATA_SIZE MAX(sizeof(app_event_t), APP_TIMER_SCHED_EVENT_DATA_SIZE) + +/******************************************************************************* * 函数声明 * *******************************************************************************/ @@ -95,14 +149,85 @@ static void m_charge_event_detect_tmr_cb(void* p_context) { // } } +static void state_machine_driver_tmr_cb(void* p_context) { // + static app_event_t appevent; + appevent.eventType = kevent_tmr_scheduler_event; + app_sched_event_put(&appevent, sizeof(appevent), app_event_process_cb); +} + /******************************************************************************* * 事件处理 * *******************************************************************************/ +static void state_machine_change_to_state(device_state_t state) { + ZLOGI("change state from %s to %s", device_state_to_str(m_device_state), device_state_to_str(state)); + m_device_state = state; + m_change_to_cur_state_tp = znordic_getpower_on_s(); +} + +static void power_on() { + SingleLeadECG_adc_module_init(); + SingleLeadECG_beep_init(); + SingleLeadECG_led_init(); + SingleLeadECG_ecg_init(); + SingleLeadECG_battery_init(); + + sample_data_mgr_init(); + dsp_mgr_init(); + + ZERROR_CHECK(app_timer_start(m_state_machine_driver_tmr, APP_TIMER_TICKS(5), NULL)); // 200HZ采样 +} +static void power_off() { + dsp_mgr_uninit(); + sample_data_mgr_uninit(); + + SingleLeadECG_battery_deinit(); + SingleLeadECG_ecg_deinit(); + SingleLeadECG_led_deinit(); + SingleLeadECG_beep_deinit(); + SingleLeadECG_adc_module_deinit(); + + ZERROR_CHECK(app_timer_stop(m_state_machine_driver_tmr)); +} +/** + * @brief 切换到充电中状态 + */ +static void state_machine__change_to_charging_state() { // + if (m_device_state == kdevice_state_home) { + // 已开机 DO Nothing + } else if (m_device_state == kdevice_state_standby) { + power_on(); + } else { + ZASSERT(false); + } + // 切换到充电显示页面 + dsp_mgr_change_to_chargingPage(); + state_machine_change_to_state(kdevice_state_charging); +} + static void app_event_process_cb(void* p_event_data, uint16_t event_size) { if (!p_event_data) return; app_event_t* p_event = (app_event_t*)p_event_data; + if (m_device_state == kdevice_state_standby) { + /******************************************************************************* + * 待机状态 * + *******************************************************************************/ + if (SingleLeadECG_battery_charge_get_state()) { + // 切换到充电状态 + state_machine__change_to_charging_state(); + } + } else if (m_device_state == kdevice_state_charging) { + /******************************************************************************* + * 充电状态 * + *******************************************************************************/ + if (!SingleLeadECG_battery_charge_get_state()) { + power_off(); + state_machine_change_to_state(kdevice_state_standby); + } + } + +#if 0 if (p_event->eventType == kplod_connected_event) { ZLOGI("plod connected"); } else if (p_event->eventType == kplod_disconnected_event) { @@ -112,10 +237,17 @@ static void app_event_process_cb(void* p_event_data, uint16_t event_size) { } if (p_event->eventType == kplod_start_charge_event) { - ZLOGI("start charge"); + /** + * @brief 检测到充电装固态 + */ + if (m_device_state == kdevice_state_standby || m_device_state == kdevice_state_home) { + state_machine__change_to_charging_state(); + } + } else if (p_event->eventType == kplod_end_charge_event) { ZLOGI("end charge"); } +#endif } /** @@ -130,12 +262,6 @@ void zdatachannel_data_handler(zdatachannel_evt_t* p_evt) { } } -/** - * @brief 测试定时器回调 - */ -static void state_machine_driver_tmr_cb(void* p_context) { // -} - /******************************************************************************* * MAIN_CODE * *******************************************************************************/