initial commit, reads dev id
Embed:
(wiki syntax)
Show/hide line numbers
MAX8614X_agc.cpp
00001 /******************************************************************************* 00002 * Author: Ismail Kose, Ismail.Kose@maximintegrated.com 00003 * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. 00004 * 00005 * Permission is hereby granted, free of charge, to any person obtaining a 00006 * copy of this software and associated documentation files (the "Software"), 00007 * to deal in the Software without restriction, including without limitation 00008 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00009 * and/or sell copies of the Software, and to permit persons to whom the 00010 * Software is furnished to do so, subject to the following conditions: 00011 * 00012 * The above copyright notice and this permission notice shall be included 00013 * in all copies or substantial portions of the Software. 00014 * 00015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00016 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00017 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00018 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES 00019 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 00020 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 00021 * OTHER DEALINGS IN THE SOFTWARE. 00022 * 00023 * Except as contained in this notice, the name of Maxim Integrated 00024 * Products, Inc. shall not be used except as stated in the Maxim Integrated 00025 * Products, Inc. Branding Policy. 00026 * 00027 * The mere transfer of this software does not imply any licenses 00028 * of trade secrets, proprietary technology, copyrights, patents, 00029 * trademarks, maskwork rights, or any other form of intellectual 00030 * property whatsoever. Maxim Integrated Products, Inc. retains all 00031 * ownership rights. 00032 ******************************************************************************* 00033 */ 00034 00035 #include "MAX8614X.h" 00036 #include <errno.h> 00037 00038 #define pr_err(fmt, args...) if(1) printf(fmt " (%s:%d)\n", ##args, __func__, __LINE__) 00039 #define pr_info(fmt, args...) if(1) printf(fmt " (%s:%d)\n", ##args, __func__, __LINE__) 00040 #define pr_debug(fmt, args...) if(0) printf(fmt " (%s:%d)\n", ##args, __func__, __LINE__) 00041 00042 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) 00043 00044 #define ILLEGAL_OUTPUT_POINTER 1 00045 #define ILLEGAL_DIODE_OUTPUT_MIN_MAX_PAIR 2 00046 #define ILLEGAL_LED_SETTING_MIN_MAX_PAIR 3 00047 #define CONSTRAINT_VIOLATION 4 00048 00049 #define MAX8614X_LED_DRIVE_CURRENT_FULL_SCALE \ 00050 (MAX8614X_MAX_LED_DRIVE_CURRENT - MAX8614X_MIN_LED_DRIVE_CURRENT) 00051 00052 #define MAX8614X_AGC_DEFAULT_LED_OUT_RANGE 15 00053 #define MAX8614X_AGC_DEFAULT_CORRECTION_COEFF 50 00054 #define MAX8614X_AGC_DEFAULT_SENSITIVITY_PERCENT 10 00055 #define MAX8614X_AGC_DEFAULT_NUM_SAMPLES_TO_AVG 25 00056 00057 #define MAX8614X_PROX_THRESHOLD_1 10000 00058 #define MAX8614X_PROX_THRESHOLD_2 40000 00059 #define MAX8614X_PROX_DEBOUNCE_SPS 2 00060 #define MAX8614X_DAQ_DEBOUNCE_SPS 20 00061 00062 #define MAX8614X_DEFAULT_DAQ_LED_CURRENT_1 40000 00063 #define MAX8614X_DEFAULT_DAQ_LED_CURRENT_2 40000 00064 #define MAX8614X_DEFAULT_DAQ_LED_CURRENT_3 40000 00065 #define MAX8614X_DEFAULT_PROX_LED_CURRENT_1 10000 00066 #define MAX8614X_DEFAULT_PROX_LED_CURRENT_2 0 00067 #define MAX8614X_DEFAULT_PROX_LED_CURRENT_3 0 00068 00069 00070 #define MAX8614X_MIN_LED_DRIVE_CURRENT 0 00071 #define MAX8614X_MAX_LED_DRIVE_CURRENT 60000 00072 00073 #define MAX8614X_MAX_PPG_DIODE_VAL ((1 << 19) - 1) 00074 #define MAX8614X_MIN_PPG_DIODE_VAL 0 00075 00076 #define MAX8614X_DEFAULT_CURRENT1 0x30 00077 #define MAX8614X_DEFAULT_CURRENT2 0 00078 #define MAX8614X_DEFAULT_CURRENT3 0 00079 00080 00081 int MAX8614X::max8614x_update_led_range( 00082 int new_range, uint8_t led_num, 00083 union led_range *led_range_settings) 00084 { 00085 int old_range; 00086 Registers reg_addr; 00087 00088 switch (led_num) { 00089 case LED_1: 00090 old_range = led_range_settings->led1; 00091 led_range_settings->led1 = new_range; 00092 reg_addr = MAX8614X_LED_RANGE1_REG; 00093 break; 00094 case LED_2: 00095 old_range = led_range_settings->led2; 00096 led_range_settings->led2 = new_range; 00097 reg_addr = MAX8614X_LED_RANGE1_REG; 00098 break; 00099 case LED_3: 00100 old_range = led_range_settings->led3; 00101 led_range_settings->led3 = new_range; 00102 reg_addr = MAX8614X_LED_RANGE1_REG; 00103 break; 00104 case LED_4: 00105 old_range = led_range_settings->led4; 00106 led_range_settings->led4 = new_range; 00107 reg_addr = MAX8614X_LED_RANGE2_REG; 00108 break; 00109 case LED_5: 00110 old_range = led_range_settings->led5; 00111 led_range_settings->led5 = new_range; 00112 reg_addr = MAX8614X_LED_RANGE2_REG; 00113 break; 00114 case LED_6: 00115 old_range = led_range_settings->led6; 00116 led_range_settings->led6 = new_range; 00117 reg_addr = MAX8614X_LED_RANGE2_REG; 00118 break; 00119 00120 default: 00121 return -EINVAL; 00122 } 00123 00124 if (old_range == new_range) 00125 return 0; 00126 00127 return writeRegister( reg_addr, 00128 led_range_settings->val[led_num < LED_4 ? 0 : 1]); 00129 } 00130 00131 int MAX8614X::max8614x_update_led_current( 00132 union led_range *led_range_settings, 00133 int led_new_val, 00134 max8614x_led_t led_num) 00135 { 00136 int ret = 0; 00137 Registers led_current_reg_addr; 00138 int led_range; 00139 uint8_t led_current_reg_val; 00140 int led_range_index = led_new_val / 25000; 00141 const int led_range_steps[] = { 00142 LED_RANGE_STEP_25uA, 00143 LED_RANGE_STEP_50uA, 00144 LED_RANGE_STEP_75uA, 00145 LED_RANGE_STEP_100uA, 00146 LED_RANGE_STEP_100uA, /* For led current greater than 100uA */ 00147 }; 00148 00149 switch(led_num) { 00150 case LED_1: 00151 led_current_reg_addr = MAX8614X_LED1_PA_REG; 00152 break; 00153 case LED_2: 00154 led_current_reg_addr = MAX8614X_LED2_PA_REG; 00155 break; 00156 case LED_3: 00157 led_current_reg_addr = MAX8614X_LED3_PA_REG; 00158 break; 00159 case LED_4: 00160 led_current_reg_addr = MAX8614X_LED4_PA_REG; 00161 break; 00162 case LED_5: 00163 led_current_reg_addr = MAX8614X_LED5_PA_REG; 00164 break; 00165 case LED_6: 00166 led_current_reg_addr = MAX8614X_LED6_PA_REG; 00167 break; 00168 default: 00169 pr_err("Invalid led number: %d\n", led_num); 00170 return -EINVAL; 00171 } 00172 00173 if (led_new_val < MAX8614X_MIN_LED_DRIVE_CURRENT 00174 || led_new_val > MAX8614X_MAX_LED_DRIVE_CURRENT) { 00175 pr_err("Invalid led value: %d\n", led_new_val); 00176 return -EINVAL; 00177 } 00178 00179 led_current_reg_val = led_new_val / led_range_steps[led_range_index]; 00180 00181 pr_debug("Updating LED%d current to %d. led_rge_idx: %d, reg_val: %.2X", 00182 led_num, led_new_val, led_range_index, led_current_reg_val); 00183 00184 ret = writeRegister(led_current_reg_addr, led_current_reg_val); 00185 if (ret < 0) 00186 return ret; 00187 00188 00189 led_range = led_range_index; 00190 pr_debug("Updating LED%d range to %d.", led_num, led_range); 00191 if (led_range > 3) 00192 led_range = 3; 00193 ret = max8614x_update_led_range( led_range, led_num, led_range_settings); 00194 if (ret < 0) 00195 return ret; 00196 return ret; 00197 } 00198 00199 int32_t agc_adj_calculator( 00200 int32_t *change_by_percent_of_range, 00201 int32_t *change_by_percent_of_current_setting, 00202 int32_t *change_led_by_absolute_count, 00203 int32_t *set_led_to_absolute_count, 00204 int32_t target_percent_of_range, 00205 int32_t correction_coefficient, 00206 int32_t allowed_error_in_percentage, 00207 int32_t current_average, 00208 int32_t number_of_samples_averaged, 00209 int32_t led_drive_current_value) 00210 { 00211 int32_t current_percent_of_range = 0; 00212 int32_t delta = 0; 00213 int32_t desired_delta = 0; 00214 int32_t current_power_percent = 0; 00215 00216 if (change_by_percent_of_range == 0 00217 || change_by_percent_of_current_setting == 0 00218 || change_led_by_absolute_count == 0 00219 || set_led_to_absolute_count == 0) 00220 return ILLEGAL_OUTPUT_POINTER; 00221 00222 if (target_percent_of_range > 90 || target_percent_of_range < 10) 00223 return CONSTRAINT_VIOLATION; 00224 00225 if (correction_coefficient > 100 || correction_coefficient < 0) 00226 return CONSTRAINT_VIOLATION; 00227 00228 if (allowed_error_in_percentage > 100 00229 || allowed_error_in_percentage < 0) 00230 return CONSTRAINT_VIOLATION; 00231 00232 #if ((MAX8614X_MAX_PPG_DIODE_VAL - MAX8614X_MIN_PPG_DIODE_VAL) <= 0 \ 00233 || (MAX8614X_MAX_PPG_DIODE_VAL < 0) || (MAX8614X_MIN_PPG_DIODE_VAL < 0)) 00234 #error "Illegal diode Min/Max Pair" 00235 #endif 00236 00237 #if ((MAX8614X_MAX_LED_DRIVE_CURRENT - MAX8614X_MIN_LED_DRIVE_CURRENT) <= 0 \ 00238 || (MAX8614X_MAX_LED_DRIVE_CURRENT < 0) || (MAX8614X_MIN_LED_DRIVE_CURRENT < 0)) 00239 #error "Illegal LED Min/Max current Pair" 00240 #endif 00241 00242 if (led_drive_current_value > MAX8614X_MAX_LED_DRIVE_CURRENT 00243 || led_drive_current_value < MAX8614X_MIN_LED_DRIVE_CURRENT) 00244 return CONSTRAINT_VIOLATION; 00245 00246 if (current_average < MAX8614X_MIN_PPG_DIODE_VAL 00247 || current_average > MAX8614X_MAX_PPG_DIODE_VAL) 00248 return CONSTRAINT_VIOLATION; 00249 00250 current_percent_of_range = 100 * 00251 (current_average - MAX8614X_MIN_PPG_DIODE_VAL) / 00252 (MAX8614X_MAX_PPG_DIODE_VAL - MAX8614X_MIN_PPG_DIODE_VAL) ; 00253 00254 delta = current_percent_of_range - target_percent_of_range; 00255 delta = delta * correction_coefficient / 100; 00256 00257 if (delta > -allowed_error_in_percentage 00258 && delta < allowed_error_in_percentage) { 00259 *change_by_percent_of_range = 0; 00260 *change_by_percent_of_current_setting = 0; 00261 *change_led_by_absolute_count = 0; 00262 *set_led_to_absolute_count = led_drive_current_value; 00263 return 0; 00264 } 00265 00266 current_power_percent = 100 * 00267 (led_drive_current_value - MAX8614X_MIN_LED_DRIVE_CURRENT) / 00268 (MAX8614X_MAX_LED_DRIVE_CURRENT - MAX8614X_MIN_LED_DRIVE_CURRENT); 00269 if (delta < 0) 00270 desired_delta = -delta * (100 - current_power_percent) / 00271 (100 - current_percent_of_range); 00272 00273 if (delta > 0) 00274 desired_delta = -delta * (current_power_percent) 00275 / (current_percent_of_range); 00276 00277 *change_by_percent_of_range = desired_delta; 00278 00279 *change_led_by_absolute_count = (desired_delta 00280 * MAX8614X_LED_DRIVE_CURRENT_FULL_SCALE / 100); 00281 *change_by_percent_of_current_setting = 00282 (*change_led_by_absolute_count * 100) 00283 / (led_drive_current_value); 00284 *set_led_to_absolute_count = led_drive_current_value 00285 + *change_led_by_absolute_count; 00286 00287 //If we are saturated, cut power in half 00288 if (current_percent_of_range >= 100) 00289 { 00290 *change_by_percent_of_range = -100; //Unknown, set fake value 00291 *change_by_percent_of_current_setting = -50; 00292 *change_led_by_absolute_count = 0 - (led_drive_current_value / 2); 00293 *set_led_to_absolute_count = led_drive_current_value / 2; 00294 } 00295 00296 return 0; 00297 } 00298 00299 void MAX8614X::ppg_auto_gain_ctrl( 00300 struct led_control *led_ctrl, 00301 uint32_t sample_cnt, int diode_data, max8614x_led_t led_num) 00302 { 00303 int ret; 00304 int diode_avg; 00305 00306 if (led_num > LED_3) /* TODO: why3? */ 00307 return; 00308 00309 led_ctrl->diode_sum[led_num] += diode_data; 00310 if (sample_cnt % led_ctrl->agc_min_num_samples == 0) { 00311 diode_avg = led_ctrl->diode_sum[led_num] 00312 / led_ctrl->agc_min_num_samples; 00313 led_ctrl->diode_sum[led_num] = 0; 00314 } else 00315 return; 00316 00317 ret = agc_adj_calculator( 00318 &led_ctrl->change_by_percent_of_range[led_num], 00319 &led_ctrl->change_by_percent_of_current_setting[led_num], 00320 &led_ctrl->change_led_by_absolute_count[led_num], 00321 &led_ctrl->led_current[led_num], 00322 led_ctrl->agc_led_out_percent, 00323 led_ctrl->agc_corr_coeff, 00324 led_ctrl->agc_sensitivity_percent, 00325 diode_avg, 00326 led_ctrl->agc_min_num_samples, 00327 led_ctrl->led_current[led_num]); 00328 if (ret) 00329 return; 00330 00331 if (led_ctrl->change_led_by_absolute_count[led_num] == 0) 00332 return; 00333 00334 ret = max8614x_update_led_current(&led_ctrl->led_range_settings, 00335 led_ctrl->led_current[led_num], led_num); 00336 if (ret < 0) 00337 pr_err("%s failed", __func__); 00338 return; 00339 } 00340 00341 void MAX8614X::max8614x_agc_handler(struct led_control *led_ctrl, 00342 int *samples) 00343 { 00344 static int ret = -1; 00345 00346 if (!led_ctrl->agc_is_enabled) 00347 return; 00348 00349 led_ctrl->sample_cnt++; 00350 ret = led_control_sm(led_ctrl, 00351 samples[DATA_TYPE_PPG1_LEDC1], 00352 led_ctrl->lpm_is_enabled); 00353 00354 if (ret == LED_DATA_ACQ) { 00355 ppg_auto_gain_ctrl(led_ctrl, 00356 led_ctrl->sample_cnt, 00357 samples[DATA_TYPE_PPG1_LEDC1], 00358 LED_1); 00359 ppg_auto_gain_ctrl(led_ctrl, 00360 led_ctrl->sample_cnt, 00361 samples[DATA_TYPE_PPG1_LEDC2], 00362 LED_2); 00363 ppg_auto_gain_ctrl(led_ctrl, 00364 led_ctrl->sample_cnt, 00365 samples[DATA_TYPE_PPG1_LEDC3], 00366 LED_3); 00367 } 00368 00369 return; 00370 } 00371 00372 int MAX8614X::led_prox_init(struct led_control *led_ctrl, char lpm) 00373 { 00374 int ret; 00375 const RegisterMap low_pm_settings[] = { 00376 { MAX8614X_PPG_CFG1_REG, MAX8614X_PPG_LED_PW_115_2_US_MASK // PPG_LED_PW = 3 (115.2us) 00377 | MAX8614X_PPG1_ADC_RGE_32768_MASK // PPG1_ADC_RGE = 3(32768nA) 00378 | MAX8614X_PPG2_ADC_RGE_32768_MASK }, // PPG2_ADC_RGE = 3(32768nA) 00379 { MAX8614X_PPG_CFG2_REG, MAX8614X_PPG_SR_25_SPS}, 00380 { MAX8614X_INT_ENABLE1_REG, MAX8614X_INT1_EN_DATA_RDY_MASK }, 00381 }; 00382 00383 led_ctrl->led_current[LED_1] = MAX8614X_DEFAULT_PROX_LED_CURRENT_1; 00384 ret = max8614x_update_led_current(&led_ctrl->led_range_settings, 00385 led_ctrl->led_current[LED_1], LED_1); 00386 00387 led_ctrl->led_current[LED_2] = MAX8614X_DEFAULT_PROX_LED_CURRENT_2; 00388 ret |= max8614x_update_led_current(&led_ctrl->led_range_settings, 00389 led_ctrl->led_current[LED_2], LED_2); 00390 00391 led_ctrl->led_current[LED_3] = MAX8614X_DEFAULT_PROX_LED_CURRENT_3; 00392 ret |= max8614x_update_led_current(&led_ctrl->led_range_settings, 00393 led_ctrl->led_current[LED_3], LED_3); 00394 00395 if (lpm) 00396 ret |= writeBlock(low_pm_settings, 00397 ARRAY_SIZE(low_pm_settings)); 00398 return ret; 00399 } 00400 00401 int MAX8614X::led_daq_init(struct led_control *led_ctrl, char lpm) 00402 { 00403 int ret; 00404 const RegisterMap non_lpm_settings[] = { 00405 { MAX8614X_PPG_CFG1_REG, MAX8614X_PPG_LED_PW_115_2_US_MASK // PPG_LED_PW = 3 (115.2us) 00406 | MAX8614X_PPG1_ADC_RGE_32768_MASK // PPG1_ADC_RGE = 3(32768nA) 00407 | MAX8614X_PPG2_ADC_RGE_32768_MASK }, // PPG2_ADC_RGE = 3(32768nA) 00408 { MAX8614X_PPG_CFG2_REG, MAX8614X_PPG_SR_100_SPS}, 00409 { MAX8614X_INT_ENABLE1_REG, MAX8614X_INT1_EN_A_FULL_MASK }, 00410 }; 00411 00412 led_ctrl->led_current[LED_1] = MAX8614X_DEFAULT_DAQ_LED_CURRENT_1; 00413 ret = max8614x_update_led_current(&led_ctrl->led_range_settings, 00414 led_ctrl->led_current[LED_1], LED_1); 00415 00416 led_ctrl->led_current[LED_2] = MAX8614X_DEFAULT_DAQ_LED_CURRENT_2; 00417 ret |= max8614x_update_led_current(&led_ctrl->led_range_settings, 00418 led_ctrl->led_current[LED_2], LED_2); 00419 00420 led_ctrl->led_current[LED_3] = MAX8614X_DEFAULT_DAQ_LED_CURRENT_3; 00421 ret |= max8614x_update_led_current(&led_ctrl->led_range_settings, 00422 led_ctrl->led_current[LED_3], LED_3); 00423 00424 if (lpm) 00425 ret |= writeBlock(non_lpm_settings, 00426 ARRAY_SIZE(non_lpm_settings)); 00427 00428 return ret; 00429 } 00430 00431 int MAX8614X::led_control_sm(struct led_control *led_ctrl, int diode_data, char lpm) 00432 { 00433 int ret = led_ctrl->state; 00434 int avg = 0; 00435 00436 led_ctrl->prox_sample_cnt++; 00437 led_ctrl->prox_sum += diode_data; 00438 00439 switch (led_ctrl->state) { 00440 case LED_PROX: 00441 if (led_ctrl->prox_sample_cnt % MAX8614X_PROX_DEBOUNCE_SPS != 0) 00442 break; 00443 00444 avg = led_ctrl->prox_sum / MAX8614X_PROX_DEBOUNCE_SPS; 00445 if (avg >= MAX8614X_PROX_THRESHOLD_1) { 00446 led_ctrl->state = LED_DATA_ACQ; 00447 ret = led_daq_init(led_ctrl, lpm); 00448 led_ctrl->prox_sample_cnt = 0; 00449 } 00450 led_ctrl->prox_sum = 0; 00451 break; 00452 00453 case LED_DATA_ACQ: 00454 if (led_ctrl->prox_sample_cnt % MAX8614X_DAQ_DEBOUNCE_SPS != 0) 00455 break; 00456 00457 avg = led_ctrl->prox_sum / MAX8614X_DAQ_DEBOUNCE_SPS; 00458 if (avg <= MAX8614X_PROX_THRESHOLD_2) { 00459 led_ctrl->state = LED_PROX; 00460 ret = led_prox_init(led_ctrl, lpm); 00461 led_ctrl->prox_sample_cnt = 0; 00462 } 00463 led_ctrl->prox_sum = 0; 00464 break; 00465 00466 default: 00467 led_ctrl->state = LED_PROX; 00468 led_ctrl->prox_sum = 0; 00469 led_ctrl->prox_sample_cnt = 0; 00470 return -EINVAL; 00471 } 00472 00473 return ret; 00474 } 00475 00476 void MAX8614X::led_control_reset(struct led_control *led_ctrl) 00477 { 00478 led_ctrl->led_current[LED_1] = led_ctrl->default_current[LED_1]; 00479 led_ctrl->led_current[LED_2] = led_ctrl->default_current[LED_2]; 00480 led_ctrl->led_current[LED_3] = led_ctrl->default_current[LED_3]; 00481 00482 memset(led_ctrl->change_by_percent_of_range, 0, 00483 sizeof(led_ctrl->change_by_percent_of_range)); 00484 memset(led_ctrl->change_by_percent_of_current_setting, 0, 00485 sizeof(led_ctrl->change_by_percent_of_range)); 00486 memset(led_ctrl->change_led_by_absolute_count, 0, 00487 sizeof(led_ctrl->change_by_percent_of_range)); 00488 memset(led_ctrl->diode_sum, 0, sizeof(led_ctrl->diode_sum)); 00489 00490 led_ctrl->agc_is_enabled = 1; 00491 led_ctrl->prox_sum = 0; 00492 led_ctrl->prox_sample_cnt = 0; 00493 led_ctrl->sample_cnt = -1; 00494 led_ctrl->state = LED_PROX; 00495 } 00496 00497 void MAX8614X::led_control_init(struct led_control *led_ctrl) 00498 { 00499 memset(led_ctrl, 0, sizeof(struct led_control)); 00500 00501 led_ctrl->default_current[LED_1] = MAX8614X_DEFAULT_CURRENT1; 00502 led_ctrl->default_current[LED_2] = MAX8614X_DEFAULT_CURRENT2; 00503 led_ctrl->default_current[LED_3] = MAX8614X_DEFAULT_CURRENT3; 00504 led_ctrl->agc_led_out_percent = MAX8614X_AGC_DEFAULT_LED_OUT_RANGE; 00505 led_ctrl->agc_corr_coeff = MAX8614X_AGC_DEFAULT_CORRECTION_COEFF; 00506 led_ctrl->agc_min_num_samples = MAX8614X_AGC_DEFAULT_NUM_SAMPLES_TO_AVG; 00507 led_ctrl->agc_sensitivity_percent = MAX8614X_AGC_DEFAULT_SENSITIVITY_PERCENT; 00508 } 00509
Generated on Fri Sep 2 2022 22:34:53 by
1.7.2