#include "app_common.h" #include "lvgl.h" #include "lv_port_disp.h" #include "lv_port_indev.h" #include "FreeRTOS.h" #include "wm_include.h" #include "lcd.h" #include "watch_face.h" #include "menu_screen.h" #include "compass_screen.h" #include "settings_screen.h" #include "i2c.h" #include "QMC5883L.h" #include "BMP280.h" #include "bma456w.h" #include "watch_peripherals.h" #include "watch_settings.h" static void date_time_cb(struct tm * const dateTime) { if(!dateTime)return; tls_get_rtc(dateTime); //APP_LOG_DEBUG("RTC time : %d:%d:%d", dateTime->tm_hour, dateTime->tm_min, dateTime->tm_sec); } WatchFace_t watchFace; MenuScreen_t menuScreen; CompassScreen_t compassScreen; SettingsScreen_t settingsScreen; struct bma4_dev bma; struct bma4_accel_config accel_conf; struct bma456w_wrist_wear_wakeup_params setting; struct bma4_int_pin_config pin_config; uint16_t int_status; static void setBrightness(uint8_t brightness) { extern LCDConfig_t LCDConfig; lcd_set_backlight(&LCDConfig, brightness); } static void setBrightnessCb(uint8_t brightness) { persistency_get_settings()->display.brightness = brightness; setBrightness(brightness); } static void setTimeCb(uint8_t hour, uint8_t minute, uint8_t second, uint8_t day, uint8_t month, uint8_t year) { struct tm timeToSet; //First we get the current time from the RTC tls_get_rtc(&timeToSet); if(hour != 0xFF) timeToSet.tm_hour = hour; if(minute != 0xFF) timeToSet.tm_min = minute; if(second != 0xFF) timeToSet.tm_sec = second; if(day != 0xFF) timeToSet.tm_mday = day; if(month != 0xFF) timeToSet.tm_mon = month; if(year != 0xFF) timeToSet.tm_year = year + 100; tls_set_rtc(&timeToSet); } static void setTimeoutCb(uint8_t timeout) { persistency_get_settings()->display.sleep_timeout = timeout; } static void setOrientationCb(uint8_t orientation) { extern LCDConfig_t LCDConfig; lcd_orientation(&LCDConfig, orientation); persistency_get_settings()->display.orientation = orientation; // Forces to redraw the full screen to avoid strange artifact lv_obj_invalidate(lv_scr_act()); } SettingsScreenAPIInterface_t settingsScreenAPIInterface = { .setBrightnessSettingsCb = setBrightnessCb, .setTimeSettingsCb = setTimeCb, .setTimeoutSettingsCb = setTimeoutCb, .setOrientationSettingsCb = setOrientationCb, }; static uint16_t angle_with_offset(uint16_t angle, uint16_t offset) { return (angle + offset) >= 360 ? angle + offset - 360 : angle + offset; } static BMA4_INTF_RET_TYPE bma4_i2c_read(uint8_t reg_addr, uint8_t *read_data, uint32_t len, void *intf_ptr) { uint8_t dev_address = *(uint8_t*)intf_ptr; return !i2c_read(dev_address, reg_addr, read_data, len); } static BMA4_INTF_RET_TYPE bma4_i2c_write(uint8_t reg_addr, const uint8_t *read_data, uint32_t len, void *intf_ptr) { uint8_t dev_address = *(uint8_t*)intf_ptr; return !i2c_write(dev_address, reg_addr, read_data, len); } static void delay_us(uint32_t period, void *intf_ptr) { (void) intf_ptr; if(period < 1000) tls_os_time_delay(1); else tls_os_time_delay(pdMS_TO_TICKS(period / 1000)); } void gfx_task(void *param) { APP_LOG_TRACE("starting"); /* Initialize the lvgl library and peripherals (display and input device)*/ lv_init(); lv_port_disp_init(); lv_port_indev_init(); /* Initialize lvgl screens */ watch_face_init(&watchFace); menu_screen_init(&menuScreen); compass_screen_init(&compassScreen); settings_screen_init(&settingsScreen); settings_screen_register_API_interface(&settingsScreen, &settingsScreenAPIInterface); watch_face_register_cb(&watchFace, &(date_time_cb)); watch_face_create(&watchFace); lv_scr_load(watchFace.display); /* Let's init the watch peripherals driver (vibration motor + battery voltage sense) */ watch_peripherals_init(27); /* Let's init the I2C interface */ i2c_init(I2C_SDA, I2C_SCL, 100000); uint8_t aliveCounter = 0; /* Init the magnetometer */ if(!QMC5883L_init()) APP_LOG_INFO("Failed to init QMC5883L"); else APP_LOG_INFO("Inited QMC5883L"); tls_os_time_delay(2); if(!QMC5883L_set_power_mode(Continuous)) APP_LOG_INFO("Failed to set QMC5883L mode"); else APP_LOG_INFO("QMC5883L Mode set"); tls_os_time_delay(2); if(!QMC5883L_configure_1(ODR_10HZ, FS_2G, OSR_512)) APP_LOG_INFO("Failed to configure 1 QMC5883L"); else APP_LOG_INFO("QMC5883L configured"); //QMC5883L_set_calibration_data(-900, 2500, -1400, 1400, 2300, 7500, 0.0); QMC5883L_set_calibration_data(4812, 8550, -3200, 20, -2200, 3500, 0.0); /* Init the BMP280 */ if(!BMP280_init()) APP_LOG_INFO("Failed to init BMP280"); else APP_LOG_INFO("Inited BMP280"); if(!BMP280_configure(BMP280_Forced, BMP280_Oversampling_x16, BMP280_Oversampling_x16, BMP280_Filter_x16, BMP280_Standby_4000MS)) APP_LOG_INFO("Failed to configure BMP280"); else APP_LOG_INFO("BMP280 configured"); /* Init the BMA456 */ bma.intf = BMA4_I2C_INTF; uint8_t dev_addr = BMA4_I2C_ADDR_SECONDARY; bma.intf_ptr = &dev_addr; bma.bus_read = &(bma4_i2c_read); bma.bus_write = &(bma4_i2c_write); bma.variant = BMA45X_VARIANT; bma.delay_us = &(delay_us); bma.read_write_len = 46; bma.perf_mode_status = BMA4_DISABLE; if(bma456w_init(&bma) == BMA4_OK) APP_LOG_INFO("BMA456 init"); else APP_LOG_INFO("Failed to init BMA456"); bma4_soft_reset(&bma); tls_os_time_delay(2); if(bma456w_write_config_file(&bma) == BMA4_OK) APP_LOG_INFO("BMA456 config ok"); else APP_LOG_INFO("BMA456 config failed"); accel_conf.odr = BMA4_OUTPUT_DATA_RATE_100HZ; accel_conf.range = BMA4_ACCEL_RANGE_2G; accel_conf.bandwidth = BMA4_ACCEL_NORMAL_AVG4; accel_conf.perf_mode = BMA4_CIC_AVG_MODE; if(bma4_set_accel_config(&accel_conf, &bma) == BMA4_OK) APP_LOG_INFO("BMA456 accel conf ok"); else APP_LOG_INFO("BMA456 accel conf failed"); if(bma4_set_accel_enable(1, &bma) == BMA4_OK) APP_LOG_INFO("BMA456 accel en ok"); else APP_LOG_INFO("BMA456 accel en failed"); bma456w_feature_enable(BMA456W_WRIST_WEAR_WAKEUP, 1, &bma); bma456w_get_wrist_wear_wakeup_param_config(&setting, &bma); APP_LOG_DEBUG("%d %d %d %d %d %d %d %d", setting.min_angle_focus, setting.min_angle_non_focus, setting.angle_landscape_right, setting.angle_landscape_left, setting.angle_portrait_up, setting.angle_portrait_down, setting.min_dur_moved, setting.min_dur_quite); if(bma4_get_int_pin_config(&pin_config, BMA4_INTR1_MAP, &bma) == BMA4_OK) APP_LOG_INFO("BMA456 get pin conf ok"); else APP_LOG_INFO("BMA456 get pin conf failed"); if(bma456w_map_interrupt(BMA4_INTR1_MAP, BMA456W_WRIST_WEAR_WAKEUP_INT, BMA4_ENABLE, &bma) == BMA4_OK) APP_LOG_INFO("BMA456 map int ok"); else APP_LOG_INFO("BMA456 map int failed"); pin_config.edge_ctrl = BMA4_EDGE_TRIGGER; pin_config.output_en = BMA4_OUTPUT_ENABLE; pin_config.lvl = BMA4_ACTIVE_LOW; pin_config.od = BMA4_PUSH_PULL; pin_config.input_en = BMA4_INPUT_DISABLE; if(bma4_set_int_pin_config(&pin_config, BMA4_INTR1_MAP, &bma) == BMA4_OK) APP_LOG_INFO("BMA456 set pin conf ok"); else APP_LOG_INFO("BMA456 set pin conf failed"); /* Once we are done with the initializing steps we don't forget to turn the backlight on ! */ setBrightness(persistency_get_settings()->display.brightness); extern LCDConfig_t LCDConfig; float temperature = 0; float pressure = 0; uint16_t battery_voltage = 0; for(;;) { lv_timer_handler(); tls_os_time_delay(5); if(compass_screen_is_in_use(&compassScreen)) { if(QMC5883L_is_data_available()) { /* QMC5883L_MData_t MDataRaw = QMC5883L_get_MFields_raw(); APP_LOG_TRACE("X %d Y %d Z %d", MDataRaw.MFieldX, MDataRaw.MFieldY, MDataRaw.MFieldZ); */ QMC5883L_MData_calibrated_t MData = QMC5883L_get_MFields_calibrated(); compass_screen_set_azimuth(&compassScreen, angle_with_offset(QMC5883L_get_azimuth(MData), 180)); } compass_screen_set_temperature(&compassScreen, temperature); } uint8_t rslt = bma456w_read_int_status(&int_status, &bma); if(rslt != BMA4_OK) APP_LOG_DEBUG("Failed to read int status"); if((BMA4_OK == rslt) && (int_status & BMA456W_WRIST_WEAR_WAKEUP_INT)) { APP_LOG_DEBUG("Wrist tilt"); } if(++aliveCounter % 200 == 0) { pressure = BMP280_get_pressure(&temperature); BMP280_trigger_measurement(); APP_LOG_DEBUG("GFX thread, temp : %0.2f °C, press : %0.2f hPa, bat : %u mV <-> %u %%", temperature, pressure/100, battery_voltage = watch_peripherals_get_battery_voltage(Battery_Unit_mV), battery_voltage_to_percentage(battery_voltage)); aliveCounter = 0; } /* Handle inactivity periods : */ if( persistency_get_settings()->display.sleep_timeout != 0 && lv_disp_get_inactive_time(NULL) > (persistency_get_settings()->display.sleep_timeout * 1000)) { // First, we disable the display backlight and we set all the peripherals in their low power mode setBrightness(0); //lcd_on(&LCDConfig, false); lcd_sleep(&LCDConfig, true); QMC5883L_set_power_mode(Standby); if(CST816D_set_power_mode()) APP_LOG_DEBUG("CST816D Sleep cmd ok"); else APP_LOG_DEBUG("CST816D Sleep cmd fail"); // Let's sleep tls_pmu_sleep_start(); // On wake up, we force the watch face to sync up with the rtc /!\ RTC update delay WTF ? tls_os_time_delay(1); watch_face_force_sync(&watchFace); lv_disp_trig_activity(NULL); QMC5883L_set_power_mode(Continuous); //lcd_on(&LCDConfig, true); lcd_sleep(&LCDConfig, false); setBrightness(persistency_get_settings()->display.brightness); } /* Throttle CPU freq down when inactive to save power or to increase responsiveness */ tls_sys_clk clk; tls_sys_clk_get(&clk); if(lv_disp_get_inactive_time(NULL) > 5000) { if(clk.cpuclk != 40) { tls_sys_clk_set(CPU_CLK_40M); APP_LOG_DEBUG("CPU 40Mhz"); } } else { if(clk.cpuclk != 160) { tls_sys_clk_set(CPU_CLK_160M); APP_LOG_DEBUG("CPU 160Mhz"); } } } }