Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: USBDevice max32630fthr
Fork of MAXREFDES220# by
SSMAX30101Comm.cpp
00001 /******************************************************************************* 00002 * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved. 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a 00005 * copy of this software and associated documentation files (the "Software"), 00006 * to deal in the Software without restriction, including without limitation 00007 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00008 * and/or sell copies of the Software, and to permit persons to whom the 00009 * Software is furnished to do so, subject to the following conditions: 00010 * 00011 * The above copyright notice and this permission notice shall be included 00012 * in all copies or substantial portions of the Software. 00013 * 00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00015 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00017 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES 00018 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 00019 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 00020 * OTHER DEALINGS IN THE SOFTWARE. 00021 * 00022 * Except as contained in this notice, the name of Maxim Integrated 00023 * Products, Inc. shall not be used except as stated in the Maxim Integrated 00024 * Products, Inc. Branding Policy. 00025 * 00026 * The mere transfer of this software does not imply any licenses 00027 * of trade secrets, proprietary technology, copyrights, patents, 00028 * trademarks, maskwork rights, or any other form of intellectual 00029 * property whatsoever. Maxim Integrated Products, Inc. retains all 00030 * ownership rights. 00031 ******************************************************************************* 00032 */ 00033 00034 #include "DSInterface.h" 00035 #include "SSMAX30101Comm.h" 00036 #include "Peripherals.h" 00037 #include "assert.h" 00038 #include "utils.h" 00039 #include "CRC8.h" 00040 00041 #define ENABLE_OS24 00042 #define ENABLE_ACCEL 00043 #define ENABLE_WHRM_ANS_SP02 00044 00045 static const char* const cmd_tbl[] = { 00046 "get_format ppg 0", 00047 "read ppg 0", 00048 "get_reg ppg", 00049 "set_reg ppg", 00050 "dump_reg ppg", 00051 "self_test ppg os24", 00052 "self_test ppg acc", 00053 }; 00054 00055 SSMAX30101Comm::SSMAX30101Comm(USBSerial *USB, SSInterface* ssInterface, DSInterface* dsInterface) 00056 :SensorComm("ppg", true), m_USB(USB), ss_int(ssInterface), ds_int(dsInterface) 00057 { 00058 max30101_mode1_data_req.data_size = SSMAX30101_MODE1_DATASIZE; 00059 max30101_mode1_data_req.callback = callback(this, &SSMAX30101Comm::max30101_data_rx); 00060 00061 whrm_mode1_data_req.data_size = SSWHRM_MODE1_DATASIZE; 00062 whrm_mode1_data_req.callback = callback(this, &SSMAX30101Comm::whrm_data_rx); 00063 00064 accel_mode1_data_req.data_size = SSACCEL_MODE1_DATASIZE; 00065 accel_mode1_data_req.callback = callback(this, &SSMAX30101Comm::accel_data_rx); 00066 00067 queue_init(&max30101_queue, max30101_queue_buf, sizeof(max30101_mode1_data), sizeof(max30101_queue_buf)); 00068 queue_init(&whrm_queue, whrm_queue_buf, sizeof(whrm_mode1_data), sizeof(whrm_queue_buf)); 00069 queue_init(&accel_queue, accel_queue_buf, sizeof(accel_mode1_data), sizeof(accel_queue_buf)); 00070 } 00071 00072 void SSMAX30101Comm::stop() 00073 { 00074 comm_mutex.lock(); 00075 ss_int->disable_irq(); 00076 data_report_mode = 0; 00077 sample_count = 0; 00078 ss_int->disable_sensor(SS_SENSORIDX_OS24); 00079 ss_int->disable_sensor(SS_SENSORIDX_ACCEL); 00080 ss_int->disable_algo(SS_ALGOIDX_WHRM); 00081 ss_int->enable_irq(); 00082 comm_mutex.unlock(); 00083 } 00084 00085 bool SSMAX30101Comm::parse_command(const char* cmd) 00086 { 00087 const char *ptr_ch = NULL; 00088 int ret; 00089 SS_STATUS status; 00090 bool recognizedCmd = false; 00091 00092 if (!ss_int) { 00093 pr_err("No SmartSensor Interface defined!"); 00094 return false; 00095 } 00096 if (!ds_int) { 00097 pr_err("No DeviceStudio Interface defined!"); 00098 return false; 00099 } 00100 00101 for (int i = 0; i < NUM_CMDS; i++) { 00102 ptr_ch = strstr(cmd, cmd_tbl[i]); 00103 00104 if (*ptr_ch) { 00105 cmd_state_t user_cmd = (cmd_state_t)i; 00106 recognizedCmd = true; 00107 00108 switch (user_cmd) { 00109 case get_format_ppg_0: 00110 { 00111 #ifdef ASCII_COMM 00112 m_USB->printf("\r\n%s format=smpleCnt,irCnt,redCnt,led3,led4,accelX,accelY,accelZ,hr,hrconf,spo2,status err=0\r\n", ptr_ch); 00113 #else 00114 m_USB->printf("\r\n%s enc=bin cs=1 format={smpleCnt,32},{irCnt,20},{redCnt,20},{led3,20},{led4,20},{accelX,14,3},{accelY,14,3},{accelZ,14,3},{hr,12,1},{hrconf,8},{spo2,11,1},{status,8} err=0\r\n", ptr_ch); 00115 #endif 00116 } break; 00117 00118 case read_ppg_0: 00119 { 00120 sample_count = 0; 00121 status = ss_int->reset(); 00122 if (status != SS_SUCCESS) { 00123 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00124 break; 00125 } 00126 00127 status = ss_int->set_data_type(SS_DATATYPE_BOTH, false); 00128 if (status != SS_SUCCESS) { 00129 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00130 break; 00131 } 00132 00133 ss_int->disable_irq(); 00134 #ifdef ENABLE_OS24 00135 status = ss_int->enable_sensor(SS_SENSORIDX_OS24, 1, &max30101_mode1_data_req); 00136 if (status != SS_SUCCESS) { 00137 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00138 ss_int->enable_irq(); 00139 break; 00140 } 00141 #endif 00142 #ifdef ENABLE_ACCEL 00143 status = ss_int->enable_sensor(SS_SENSORIDX_ACCEL, 1, &accel_mode1_data_req); 00144 if (status != SS_SUCCESS) { 00145 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00146 ss_int->enable_irq(); 00147 break; 00148 } 00149 00150 #endif 00151 00152 #ifdef ENABLE_WHRM_ANS_SP02 00153 status = ss_int->enable_algo(SS_ALGOIDX_WHRM, 1, &whrm_mode1_data_req); 00154 if (status != SS_SUCCESS) { 00155 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00156 } else { 00157 comm_mutex.lock(); 00158 data_report_mode = read_ppg_0; 00159 comm_mutex.unlock(); 00160 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_SUCCESS); 00161 } 00162 #endif 00163 ss_int->enable_irq(); 00164 } break; 00165 00166 case get_reg_ppg: 00167 { 00168 uint8_t addr; 00169 uint32_t val; 00170 00171 ret = parse_get_reg_cmd(ptr_ch, sensor_type, &addr); 00172 if (!ret) { 00173 status = ss_int->get_reg(SS_SENSORIDX_OS24, addr, &val); 00174 if (status == SS_SUCCESS) { 00175 m_USB->printf("\r\n%s reg_val=%02X err=%d\r\n", ptr_ch, (uint8_t)val, COMM_SUCCESS); 00176 } else { 00177 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00178 } 00179 } else { 00180 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_INVALID_PARAM); 00181 } 00182 00183 } break; 00184 00185 case set_reg_ppg: 00186 { 00187 uint8_t addr; 00188 uint8_t val; 00189 00190 ret = parse_set_reg_cmd(ptr_ch, sensor_type, &addr, &val); 00191 if (!ret) { 00192 status = ss_int->set_reg(SS_SENSORIDX_OS24, addr, val, SSMAX30101_REG_SIZE); 00193 if (status == SS_SUCCESS) { 00194 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_SUCCESS); 00195 } else { 00196 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00197 } 00198 } else { 00199 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_INVALID_PARAM); 00200 } 00201 00202 } break; 00203 00204 case dump_reg_ppg: 00205 { 00206 int num_regs; 00207 status = ss_int->dump_reg(SS_SENSORIDX_OS24, ®_vals[0], ARRAY_SIZE(reg_vals), &num_regs); 00208 if (status == SS_SUCCESS) { 00209 int len = 0; 00210 bool comma = false; 00211 for (int reg = 0; reg < num_regs; reg++) { 00212 if (comma) { 00213 len += snprintf(&charbuf[0] + len, sizeof(charbuf) - len - 1, ","); 00214 } 00215 len += snprintf(&charbuf[0] + len, sizeof(charbuf) - len - 1, 00216 "{%X,%lX}", reg_vals[reg].addr, reg_vals[reg].val); 00217 comma = true; 00218 } 00219 charbuf[len] = '\0'; 00220 00221 m_USB->printf("\r\n%s reg_val=%s err=%d\r\n", ptr_ch, &charbuf[0], COMM_SUCCESS); 00222 00223 } else { 00224 m_USB->printf("\r\n%s err=%d\r\n", ptr_ch, COMM_GENERAL_ERROR); 00225 } 00226 00227 } break; 00228 00229 case self_test_ppg_os24: 00230 { 00231 ret = selftest_max30101(); 00232 m_USB->printf("%s selftest_max30101: err=<%d>\r\n", ptr_ch, ret); 00233 } break; 00234 00235 case self_test_ppg_acc: 00236 { 00237 ret = selftest_accelerometer(); 00238 m_USB->printf("%s selftest_accelerometer: err=<%d>\r\n", ptr_ch, ret); 00239 } break; 00240 00241 default: 00242 { 00243 assert_msg(false, "Invalid switch case!"); 00244 } break; 00245 00246 } 00247 break; 00248 } 00249 } 00250 00251 return recognizedCmd; 00252 } 00253 00254 void SSMAX30101Comm::max30101_data_rx(uint8_t* data_ptr) 00255 { 00256 max30101_mode1_data sample; 00257 sample.led1 = (data_ptr[0] << 16) | (data_ptr[1] << 8) | data_ptr[2]; 00258 sample.led2 = (data_ptr[3] << 16) | (data_ptr[4] << 8) | data_ptr[5]; 00259 sample.led3 = (data_ptr[6] << 16) | (data_ptr[7] << 8) | data_ptr[8]; 00260 sample.led4 = (data_ptr[9] << 16) | (data_ptr[10] << 8) | data_ptr[11]; 00261 00262 pr_info("led1=%.6X led2=%.6X led3=%.6X led4=%.6X\r\n", sample.led1, sample.led2, sample.led3, sample.led4); 00263 00264 enqueue(&max30101_queue, &sample); 00265 } 00266 00267 void SSMAX30101Comm::whrm_data_rx(uint8_t* data_ptr) 00268 { 00269 //See API doc for data format 00270 whrm_mode1_data sample; 00271 sample.hr = (data_ptr[0] << 8) | data_ptr[1]; 00272 sample.hr_conf = data_ptr[2]; 00273 sample.spo2 = (data_ptr[3] << 8) | data_ptr[4]; 00274 sample.status = data_ptr[5]; 00275 00276 pr_info("hr=%.1f conf=%d spo2=%d status=%d\r\n", (float)sample.hr / 10.0, sample.hr_conf, sample.spo2, sample.status); 00277 enqueue(&whrm_queue, &sample); 00278 } 00279 void SSMAX30101Comm::accel_data_rx(uint8_t* data_ptr) 00280 { 00281 //See API doc for data format 00282 accel_mode1_data sample; 00283 sample.x = (data_ptr[0] << 8) | data_ptr[1]; 00284 sample.y = (data_ptr[2] << 8) | data_ptr[3]; 00285 sample.z = (data_ptr[4] << 8) | data_ptr[5]; 00286 00287 enqueue(&accel_queue, &sample); 00288 } 00289 00290 int SSMAX30101Comm::data_report_execute(char* buf, int size) 00291 { 00292 uint8_t tmp_report_mode; 00293 max30101_mode1_data max30101_sample = { 0 }; 00294 whrm_mode1_data whrm_sample = { 0 }; 00295 accel_mode1_data accel_sample = { 0 }; 00296 int16_t data_len = 0; 00297 00298 if (size < 0) 00299 return 0; 00300 00301 if (!is_enabled()) 00302 return 0; 00303 00304 comm_mutex.lock(); 00305 tmp_report_mode = data_report_mode; 00306 comm_mutex.unlock(); 00307 00308 switch (tmp_report_mode) { 00309 case read_ppg_0: 00310 { 00311 if ( 00312 00313 #ifdef ENABLE_OS24 00314 queue_len(&max30101_queue) > 0 00315 #endif 00316 #ifdef ENABLE_ACCEL 00317 && queue_len(&accel_queue) > 0 00318 #endif 00319 #ifdef ENABLE_WHRM_ANS_SP02 00320 && queue_len(&whrm_queue) > 0 00321 #endif 00322 ) 00323 { 00324 #ifdef ENABLE_OS24 00325 dequeue(&max30101_queue, &max30101_sample); 00326 #endif 00327 #ifdef ENABLE_ACCEL 00328 dequeue(&accel_queue, &accel_sample); 00329 #endif 00330 #ifdef ENABLE_WHRM_ANS_SP02 00331 dequeue(&whrm_queue, &whrm_sample); 00332 #endif 00333 #ifdef ASCII_COMM 00334 data_len = snprintf(buf, size - 1, "%u,%lu,%lu,%lu,%lu,%.3f,%.3f,%.3f,%.1f,%d,%.1f,%d\r\n", 00335 sample_count++, 00336 max30101_sample.led1, 00337 max30101_sample.led2, 00338 max30101_sample.led3, 00339 max30101_sample.led4, 00340 accel_sample.x * 0.001, 00341 accel_sample.y * 0.001, 00342 accel_sample.z * 0.001, 00343 whrm_sample.hr * 0.1, 00344 whrm_sample.hr_conf, 00345 whrm_sample.spo2 * 0.1, 00346 whrm_sample.status); 00347 #else 00348 assert_msg(((uint32_t)size > sizeof(ds_pkt_data_mode1)), "data_report_execute buffer too small"); 00349 ds_pkt_data_mode1* pkt = (ds_pkt_data_mode1*)buf; 00350 pkt->start_byte = DS_BINARY_PACKET_START_BYTE; 00351 pkt->led1 = max30101_sample.led1; 00352 pkt->led2 = max30101_sample.led2; 00353 pkt->led3 = max30101_sample.led3; 00354 pkt->led4 = max30101_sample.led4; 00355 pkt->x = accel_sample.x; 00356 pkt->y = accel_sample.y; 00357 pkt->z = accel_sample.z; 00358 pkt->hr = whrm_sample.hr; 00359 pkt->spo2 = whrm_sample.spo2; 00360 pkt->status = whrm_sample.status; 00361 pkt->crc8 = crc8((uint8_t*)pkt, sizeof(ds_pkt_data_mode1) - sizeof(uint8_t)); 00362 data_len = sizeof(ds_pkt_data_mode1); 00363 #endif 00364 } 00365 } break; 00366 00367 default: 00368 return 0; 00369 } 00370 00371 if (data_len < 0) { 00372 pr_err("snprintf console_tx_buf failed"); 00373 } else if (data_len > size) { 00374 pr_err("buffer is insufficient to hold data"); 00375 } 00376 00377 return data_len; 00378 } 00379 00380 // TODO: convert this to PPG sensor test 00381 int SSMAX30101Comm::selftest_max30101(){ 00382 int ret; 00383 uint8_t test_result; 00384 bool test_failed = false; 00385 m_USB->printf("starting selftest_max30101\r\n"); 00386 // configure mfio pin for self test 00387 ss_int->mfio_selftest(); 00388 ret = ss_int->self_test(SS_SENSORIDX_OS24, &test_result, 500); 00389 if(ret != SS_SUCCESS){ 00390 m_USB->printf("ss_int->self_test(SS_SENSORIDX_OS24, &test_result) has failed err<-1>\r\n"); 00391 test_failed = true; 00392 } 00393 // reset mfio pin to old state 00394 if(!ss_int->reset_mfio_irq()){ 00395 m_USB->printf("smart sensor reset_mfio_irq has failed err<-1>\r\n"); 00396 test_failed = true; 00397 } 00398 // reset the sensor to turn off the LED 00399 ret = ss_int->reset(); 00400 if(test_failed | !self_test_result_evaluate("selftest_max30101", test_result)){ 00401 return -1; 00402 }else{ 00403 return SS_SUCCESS; 00404 } 00405 } 00406 00407 int SSMAX30101Comm::selftest_accelerometer(){ 00408 int ret; 00409 uint8_t test_result; 00410 bool test_failed = false; 00411 m_USB->printf("starting selftest_accelerometer\r\n"); 00412 ret = ss_int->self_test(SS_SENSORIDX_ACCEL, &test_result, 1000); 00413 if(ret != SS_SUCCESS){ 00414 m_USB->printf("ss_int->self_test(SS_SENSORIDX_ACCEL, &test_result) has failed err<-1>\r\n"); 00415 test_failed = true; 00416 } 00417 // reset the sensor to turn off the LED 00418 ret = ss_int->reset(); 00419 if(ret != SS_SUCCESS){ 00420 m_USB->printf("smart sensor reset has failed err<-1>\r\n"); 00421 test_failed = true; 00422 } 00423 if(test_failed | !self_test_result_evaluate("selftest_accelerometer", test_result)){ 00424 return -1; 00425 }else{ 00426 return SS_SUCCESS; 00427 } 00428 } 00429 00430 bool SSMAX30101Comm::self_test_result_evaluate(const char *message, uint8_t result){ 00431 // check i2c response status 00432 if(result != 0x00){ 00433 m_USB->printf("%s has failed % 02X err<-1>\r\n", message, result); 00434 if((result & FAILURE_COMM)) 00435 m_USB->printf("%s communication has failed err<-1>\r\n", message); 00436 if(result & FAILURE_INTERRUPT) 00437 m_USB->printf("%s interrupt pin check has failed err<-1>\r\n", message); 00438 return false; 00439 } 00440 return true; 00441 }
Generated on Mon Jul 18 2022 23:37:28 by
