class pah8011 for mbed
pah8011/pah_driver_8011.c
- Committer:
- bell_huang
- Date:
- 2019-01-23
- Revision:
- 6:d196b612b14a
- Parent:
- 0:242cf8f28bf2
File content as of revision 6:d196b612b14a:
/*============================================================================== * Edit History * * This section contains comments describing changes made to the module. Notice * that changes are listed in reverse chronological order. Please use ISO format * for dates. * * when version who what, where, why * ---------- ------ --- ----------------------------------------------------------- * 2016-11-09 1009 bh - Update pah_driver_8011_reg.h. * -- Fix green LEDs were turned on weakly when touch mode. * 2016-10-20 1008 bh - Merge v1007 + v10052. * -- Move some code to new file pah_8011_internal for reuse. * -- Support variable channel number settings. * -- Support ET device package (former driver support ES device package only). * -- Add function: pah_get_fifo_ch_num(). * -- Add flags: pah_stream_e, pah_intshape_pulse_type_e, pah_powerdown_int_status_e. * -- Remove debug_trace debug log because it's rare to disable. * 2016-10-18 10052 bh - Fix pah_init didn't shutdown. * 2016-09-08 10051 bh - Created branch v10051 * - Add functions: pah_init_with_flags(). * - Add pah_ppg_led_on_e flag. * - Replace all update flag reg setting with _pah8011_update_flag(). * - Remove useless comments * 2016-07-07 1004 bh - Move reg array settings to new source files. * 2016-07-01 1003 bh - Fix undefined behaviors when the device wakes up from power down mode. Delay is necessary for the internal controller to reset the whole system and then the device is ready for the upcoming operations. * 2016-06-07 1002 bh - Add functions: pah_set_mode(), pah_run_device(). * - Add enum: pah_device. * 2016-04-29 1001 bh - Add PPG 200Hz modes. * - Add helper functions: pah_is_ppg_mode(), pah_is_ppg_20hz_mode(), pah_fifo_data_num_per_ch(). * - Add pah_stop_mode * - Remove pah_suspend_mode. * - Fix setting pah_set_report_sample_num_per_ch() after enter_mode() causes bad behavior. * 2016-04-14 1000 bh - Add version. * 2016-04-12 bh - Add license information and revision information. * - Modify to reduce the reaction time of touch/no-touch detect. * 2016-04-07 bh - Initial revision. ==============================================================================*/ #include "pah_driver.h" // pah #include "pah_driver_8011_reg_array.h" #include "pah_8011_internal.h" #include "pah_comm.h" // platform support #include "pah_platform_functions.h" /*============================================================================ VALUE MACRO DEFINITIONS ============================================================================*/ #define PAH_DRIVER_8011_VERSION 1009 #define DEFAULT_REPORT_SAMPLE_NUM_PER_CH 20 #define MAX_SAMPLES_PER_READ 396 #define MAX_CH_NUM 3 #define BYTES_PER_SAMPLE 4 #define PPG_FRAME_INTERVAL_MS 60 // 50 * 120% /*============================================================================ OPTION MACRO DEFINITIONS ============================================================================*/ //#define ENABLE_FIFO_CHECKSUM /*============================================================================ TYPE DEFINITIONS ============================================================================*/ typedef enum { pah_fifo_freq_none, pah_fifo_freq_20hz, pah_fifo_freq_200hz, } pah_fifo_freq; typedef struct { uint32_t report_sample_num_per_ch; uint8_t is_int2_as_touch_flag; } pah8011_property_s; typedef struct { // fifo data uint8_t fifo_data[MAX_SAMPLES_PER_READ * BYTES_PER_SAMPLE]; // update every task uint32_t fifo_data_num_per_ch; // flags pah_flags_s flags; // report ppg data void* user_data; pah_report_fifo_handle fp_report_fifo_handler; // private members uint8_t has_started_tg; uint8_t has_started_ppg; uint8_t has_started_touch; uint8_t fifo_data_contain_touch_bit; // read only properties pah_mode mode; uint8_t touch_flag; //// flag options // pah_ppg_led_on_e uint8_t is_led_on; // properties pah8011_property_s prop_curr; pah8011_property_s prop_next; } pah8011_state_s; /*============================================================================ PRIVATE GLOBAL VARIABLES ============================================================================*/ static bool g_init = false; static pah8011_state_s g_state; /*============================================================================ PRIVATE FUNCTION PROTOTYPES ============================================================================*/ //// pure functions static bool _pah_is_ppg_mode(pah_mode mode); static pah_fifo_freq _pah_mode_to_freq(pah_mode mode); static bool _pah_is_diff_fifo_freq(pah_mode lhs_mode, pah_mode rhs_mode); //// pah8011 static bool _pah8011_init(void); static bool _pah8011_shutdown(void); static bool _pah8011_startup(void); static bool _pah8011_start_tg(void); static bool _pah8011_start_ppg(void); static bool _pah8011_stop_ppg(void); static bool _pah8011_start_touch(void); static bool _pah8011_stop_touch(void); static bool _pah8011_clear_fifo_and_int_req(void); static bool _pah8011_update_report_num(void); static bool _pah8011_set_report_num(uint32_t samples_per_read); static bool _pah8011_turn_led(bool on); #if defined(ENABLE_FIFO_CHECKSUM) static bool _pah8011_cks(uint8_t *fifo, uint32_t samples_read, uint32_t cks_read); #endif // ENABLE_FIFO_CHECKSUM static bool _pah8011_read_touch_flag(uint8_t *touch_flag); static pah_ret _pah8011_task_dri(uint8_t int_req, uint32_t ch_num); static pah_ret _pah8011_task_polling(uint8_t int_req, uint32_t ch_num); /*============================================================================ PUBLIC FUNCTION DEFINITIONS ============================================================================*/ void pah_flags_default(pah_flags_s *flags) { if (!flags) return; memset(flags, 0, sizeof(*flags)); } bool pah_init(void) { pah_flags_s flags; pah_flags_default(&flags); return pah_init_with_flags(&flags); } bool pah_init_with_flags(const pah_flags_s *flags) { pah_ret ret = pah_err_unknown; debug_printf(">>>> pah_init() \n"); if (!flags) { ret = pah_err_invalid_argument; goto FAIL; } pah_deinit(); memset(&g_state, 0, sizeof(g_state)); // set flags memcpy(&g_state.flags, flags, sizeof(g_state.flags)); // set default mode g_state.mode = pah_stop_mode; // properties g_state.prop_curr.report_sample_num_per_ch = 0; g_state.prop_next.report_sample_num_per_ch = DEFAULT_REPORT_SAMPLE_NUM_PER_CH; ret = pah_8011_verify_product_id(); if (PAH_FAILED(ret)) { ret = pah_8011_startup(); if (PAH_FAILED(ret)) goto FAIL; ret = pah_8011_verify_product_id(); if (PAH_FAILED(ret)) goto FAIL; } // register arrays if (!pah8011_reg_array_init()) { ret = pah_err_platform_fail; goto FAIL; } // init then shutdown ret = pah_8011_device_init(); if (PAH_FAILED(ret)) goto FAIL; ret = pah_8011_shutdown(g_state.flags.powerdown_int_status); if (PAH_FAILED(ret)) goto FAIL; g_init = true; debug_printf("<<<< pah_init() \n"); return true; FAIL: debug_printf_1("pah_init() faied. ret = %d \n", ret); return false; } void pah_deinit(void) { if (!g_init) return; debug_printf(">>>> pah_deinit() \n"); _pah8011_shutdown(); g_init = false; debug_printf("<<<< pah_deinit() \n"); } bool pah_enter_mode(pah_mode mode) { if (!pah_set_mode(mode)) goto FAIL; switch (mode) { case pah_touch_mode: if (!pah_run_device(pah_device_touch, true)) goto FAIL; break; case pah_ppg_mode: case pah_ppg_200hz_mode: if (!pah_run_device(pah_device_ppg, true)) goto FAIL; break; case pah_ppg_touch_mode: case pah_ppg_200hz_touch_mode: if (!pah_run_device(pah_device_ppg, true)) goto FAIL; if (!pah_run_device(pah_device_touch, true)) goto FAIL; break; default: break; } return true; FAIL: debug_printf("pah_enter_mode(). fail \n"); return false; } bool pah_set_mode(pah_mode mode) { pah_ret ret = pah_err_unknown; debug_printf_2(">>>> pah_set_mode(), mode = %d -> %d \n", g_state.mode, mode); if (g_state.mode == mode) { debug_printf("<<<< pah_set_mode() \n"); return true; } if (g_state.mode == pah_stop_mode) { if (!_pah8011_startup()) goto FAIL; if (!_pah8011_init()) goto FAIL; // update properties if (!_pah8011_update_report_num()) goto FAIL; g_state.prop_curr.is_int2_as_touch_flag = g_state.prop_next.is_int2_as_touch_flag; } if (pah_stop_mode == mode) { if (!_pah8011_shutdown()) goto FAIL; } else { switch (mode) { case pah_touch_mode: { _pah8011_stop_ppg(); if (_pah_is_ppg_mode(g_state.mode) && !_pah8011_clear_fifo_and_int_req()) goto FAIL; if (!pah8011_reg_array_load_mode(pah8011_reg_array_mode_touch)) goto FAIL; } break; case pah_ppg_mode: { _pah8011_stop_touch(); if (_pah_is_diff_fifo_freq(g_state.mode, mode)) { if (!_pah8011_stop_ppg()) goto FAIL; if (!_pah8011_clear_fifo_and_int_req()) goto FAIL; } if (!pah8011_reg_array_load_mode(pah8011_reg_array_mode_ppg_20hz)) goto FAIL; if (!_pah8011_update_report_num()) goto FAIL; } break; case pah_ppg_200hz_mode: { _pah8011_stop_touch(); if (_pah_is_diff_fifo_freq(g_state.mode, mode)) { if (!_pah8011_stop_ppg()) goto FAIL; if (!_pah8011_clear_fifo_and_int_req()) goto FAIL; } if (!pah8011_reg_array_load_mode(pah8011_reg_array_mode_ppg_200hz)) goto FAIL; if (!_pah8011_update_report_num()) goto FAIL; } break; case pah_ppg_touch_mode: { if (_pah_is_diff_fifo_freq(g_state.mode, mode)) { if (!_pah8011_stop_ppg()) goto FAIL; if (!_pah8011_clear_fifo_and_int_req()) goto FAIL; } if (!pah8011_reg_array_load_mode(pah8011_reg_array_mode_ppg_20hz)) goto FAIL; if (!_pah8011_update_report_num()) goto FAIL; } break; case pah_ppg_200hz_touch_mode: { if (_pah_is_diff_fifo_freq(g_state.mode, mode)) { if (!_pah8011_stop_ppg()) goto FAIL; if (!_pah8011_clear_fifo_and_int_req()) goto FAIL; } if (!pah8011_reg_array_load_mode(pah8011_reg_array_mode_ppg_200hz)) goto FAIL; if (!_pah8011_update_report_num()) goto FAIL; } break; default: goto FAIL; } // pah_ppg_led_on_e flag if (g_state.flags.ppg_led_on == pah_ppg_led_on_deferred) { if (!_pah_is_ppg_mode(g_state.mode) && _pah_is_ppg_mode(mode)) { if (!_pah8011_turn_led(false)) goto FAIL; g_state.is_led_on = false; } } ret = pah_8011_update_flag(); if (PAH_FAILED(ret)) goto FAIL; } g_state.mode = mode; debug_printf("<<<< pah_set_mode() \n"); return true; FAIL: debug_printf("pah_set_mode(). fail \n"); return false; } bool pah_run_device(pah_device device, bool enable) { pah_ret ret = pah_err_unknown; debug_printf_2(">>>> pah_run_device(), device = %d, enable = %d \n", device, enable); switch (device) { case pah_device_ppg: if (enable) { if (!_pah8011_update_report_num()) goto FAIL; if (!_pah8011_start_ppg()) goto FAIL; } else { if (!_pah8011_stop_ppg()) goto FAIL; } break; case pah_device_touch: if (enable) { if (!_pah8011_start_touch()) goto FAIL; } else { if (!_pah8011_stop_touch()) goto FAIL; } break; default: goto FAIL; } ret = pah_8011_update_flag(); if (PAH_FAILED(ret)) goto FAIL; if (enable) { if (!_pah8011_start_tg()) goto FAIL; } debug_printf("<<<< pah_run_device() \n"); return true; FAIL: debug_printf("pah_run_device(). fail \n"); return false; } pah_mode pah_query_mode(void) { return g_state.mode; } bool pah_is_ppg_mode(void) { return _pah_is_ppg_mode(g_state.mode); } bool pah_is_ppg_20hz_mode(void) { return g_state.mode == pah_ppg_mode || g_state.mode == pah_ppg_touch_mode; } pah_ret pah_task(void) { pah_ret ret = pah_err_unknown; uint8_t int_req = 0; debug_printf(">>>> pah_task() \n"); // reset g_state.fifo_data_num_per_ch = 0; // read interrupt if (!pah_comm_write(0x7F, 0x02)) //Bank 2 { ret = pah_err_platform_fail; goto FAIL; } if (!pah_comm_read(0x1B, &int_req)) //read interrupt status { ret = pah_err_platform_fail; goto FAIL; } debug_printf_1("pah_task(). int_req = %d \n", int_req); // fifo overflow if (int_req & 0x04) { // Usually happens when pah_task() was too late to be called. // Troubleshooting: Record timestamps before every pah_task() calls, the interval of // different pah_task() calls should be regular. debug_printf("pah_task(). fifo overflow \n"); ret = pah_err_fifo_overflow; goto FAIL; } // underflow interrupt if (int_req & 0x08) { debug_printf("pah_task(). fifo underflow \n"); ret = pah_err_fifo_underflow; goto FAIL; } // task { uint32_t ch_num = pah_get_fifo_ch_num(); if (!ch_num) { debug_printf("pah_task(). ch_num should never be 0 \n"); ret = pah_err_invalid_program; goto FAIL; } if (pah_stream_polling == g_state.flags.stream) ret = _pah8011_task_polling(int_req, ch_num); else ret = _pah8011_task_dri(int_req, ch_num); } if (ret != pah_success && ret != pah_pending) goto FAIL; if (int_req) { // clear interrupt if (!pah_comm_write(0x7F, 0x02)) //Bank 2 { ret = pah_err_platform_fail; goto FAIL; } if (!pah_comm_write(0x1b, int_req)) //clear interrupt { ret = pah_err_platform_fail; goto FAIL; } //debug_printf("pah_task(). clear interrupt, int_req = %d \n", int_req); } // update properties if (!_pah8011_update_report_num()) { ret = pah_err_platform_fail; goto FAIL; } // pah_ppg_led_on_e flag if (g_state.flags.ppg_led_on == pah_ppg_led_on_deferred && !g_state.is_led_on) { if (g_state.touch_flag && pah_is_ppg_mode()) { if (!_pah8011_turn_led(true)) { ret = pah_err_platform_fail; goto FAIL; } ret = pah_8011_update_flag(); if (PAH_FAILED(ret)) goto FAIL; g_state.is_led_on = true; } } debug_printf("<<<< pah_task() \n"); return ret; FAIL: debug_printf_1("pah_task(). fail, ret = %d \n", ret); return ret; } uint8_t* pah_get_fifo_data(void) { return g_state.fifo_data; } uint32_t pah_fifo_data_num_per_ch(void) { return g_state.fifo_data_num_per_ch; } bool pah_has_fifo_data(void) { return g_state.fifo_data_num_per_ch > 0; } uint32_t pah_get_fifo_ch_num(void) { return pah8011_get_ppg_ch_num(); } bool pah_is_touched(void) { return g_state.touch_flag > 0; } void pah_clear_fifo_data(void) { memset(g_state.fifo_data, 0, sizeof(g_state.fifo_data)); } void pah_set_report_fifo_callback(pah_report_fifo_handle fp_handler, void* user_data) { debug_printf("==== pah_set_report_fifo_callback() \n"); g_state.fp_report_fifo_handler = fp_handler; g_state.user_data = user_data; } uint16_t pah_get_i2c_slave_addr(void) { debug_printf("==== pah_get_i2c_slave_addr() \n"); return PAH_I2C_SLAVE_ID; } bool pah_set_int2_as_touch_flag(bool enable) { debug_printf_1("==== pah_set_int2_as_touch_flag(). enable = %d \n", enable); g_state.prop_next.is_int2_as_touch_flag = enable; return true; } void pah_set_report_sample_num_per_ch(uint32_t report_sample_num_per_ch) { static const uint32_t MIN_REPORT_SAMPLE_NUM_PER_CH = 1; const uint32_t MAX_REPORT_SAMPLE_NUM_PER_CH = pah_get_max_report_sample_num_per_ch(); if (report_sample_num_per_ch < MIN_REPORT_SAMPLE_NUM_PER_CH) report_sample_num_per_ch = MIN_REPORT_SAMPLE_NUM_PER_CH; else if (report_sample_num_per_ch > MAX_REPORT_SAMPLE_NUM_PER_CH) report_sample_num_per_ch = MAX_REPORT_SAMPLE_NUM_PER_CH; g_state.prop_next.report_sample_num_per_ch = report_sample_num_per_ch; debug_printf_1("pah_set_report_sample_num_per_ch(). report_sample_num_per_ch = %d \n", report_sample_num_per_ch); } uint32_t pah_get_report_sample_num_per_ch(void) { return g_state.prop_curr.report_sample_num_per_ch; } uint32_t pah_get_max_report_sample_num_per_ch(void) { return MAX_SAMPLES_PER_READ / MAX_CH_NUM; } uint32_t pah_get_bytes_per_sample(void) { return BYTES_PER_SAMPLE; } bool pah_verify_product_id(void) { uint8_t data = 0; debug_printf(">>>> pah_verify_product_id() \n"); if (!pah_comm_write(0x7F, 0x00)) //bank0 goto FAIL; if (!pah_comm_read(0x00, &data)) goto FAIL; if (data != 0x11) { debug_printf_1("pah_verify_product_id() fail. data = %d \n", data); goto FAIL; } debug_printf("<<<< pah_verify_product_id() \n"); return true; FAIL: debug_printf("pah_verify_product_id error \n"); return false; } pah_ret pah_read_touch_flag(bool *is_touched) { uint8_t touch_flag = 0; debug_printf(">>>> pah_read_touch_flag() \n"); if (!is_touched) { debug_printf("pah_read_touch_flag() fail. invalid argument \n"); return pah_err_invalid_argument; } if (!_pah8011_read_touch_flag(&touch_flag)) return pah_err_platform_fail; *is_touched = touch_flag > 0; debug_printf_1("<<<< pah_read_touch_flag(). is_touched = %d \n", *is_touched); return pah_success; } /*============================================================================ Pure functions ============================================================================*/ static bool _pah_is_ppg_mode(pah_mode mode) { return mode == pah_ppg_mode || mode == pah_ppg_200hz_mode || mode == pah_ppg_touch_mode || mode == pah_ppg_200hz_touch_mode; } static pah_fifo_freq _pah_mode_to_freq(pah_mode mode) { pah_fifo_freq result = pah_fifo_freq_none; switch (mode) { case pah_ppg_mode: case pah_ppg_touch_mode: result = pah_fifo_freq_20hz; break; case pah_ppg_200hz_mode: case pah_ppg_200hz_touch_mode: result = pah_fifo_freq_200hz; break; default: result = pah_fifo_freq_none; break; } return result; } static bool _pah_is_diff_fifo_freq(pah_mode lhs_mode, pah_mode rhs_mode) { pah_fifo_freq lhs_freq = _pah_mode_to_freq(lhs_mode); pah_fifo_freq rhs_freq = _pah_mode_to_freq(rhs_mode); if (lhs_freq == pah_fifo_freq_none || rhs_freq == pah_fifo_freq_none) return false; return lhs_freq != rhs_freq; } /*============================================================================ PRIVATE FUNCTION DEFINITIONS ============================================================================*/ static bool _pah8011_init(void) { pah_ret ret = pah_err_unknown; uint8_t data = 0; debug_printf_1("_pah8011_init(), PAH8011 driver[v%d] \n", PAH_DRIVER_8011_VERSION); //// reg init settings pah8011_reg_array_load_init(); //// touch init ret = pah_8011_write_touch_package_setting(); if (PAH_FAILED(ret)) goto FAIL; //// other init settings if (!pah_comm_write(0x7F, 0x01)) //bank1 goto FAIL; // disable ch a if (!pah_comm_read(0x21, &data)) goto FAIL; PAH_CLEAR_BIT(data, 0); if (!pah_comm_write(0x21, data)) goto FAIL; // disable ch b if (!pah_comm_read(0x23, &data)) goto FAIL; PAH_CLEAR_BIT(data, 0); if (!pah_comm_write(0x23, data)) goto FAIL; // disable ch c if (!pah_comm_read(0x25, &data)) goto FAIL; PAH_CLEAR_BIT(data, 0); if (!pah_comm_write(0x25, data)) goto FAIL; // touch interrupt if (!pah_comm_read(0x36, &data)) goto FAIL; PAH_SET_BIT(data, 0); PAH_CLEAR_BIT(data, 1); if (!pah_comm_write(0x36, data)) goto FAIL; // mask if (!pah_comm_read(0x37, &data)) goto FAIL; PAH_SET_BIT(data, 1); if (!pah_comm_write(0x37, data)) goto FAIL; // touch bit if (!pah_comm_read(0x5A, &data)) goto FAIL; g_state.fifo_data_contain_touch_bit = PAH_CHECK_BIT(data, 0); // intshape_pulse_type if (g_state.flags.intshape_pulse_type == pah_intshape_pulse_type_level) { if (!pah_comm_write(0x7F, 0x01)) //Bank 1 goto FAIL; if (!pah_comm_read(0x35, &data)) goto FAIL; PAH_CLEAR_BIT(data, 1); if (!pah_comm_write(0x35, data)) goto FAIL; } ret = pah_8011_update_flag(); if (PAH_FAILED(ret)) goto FAIL; return true; FAIL: debug_printf("_pah8011_init fail \n"); return false; } static bool _pah8011_shutdown(void) { pah_ret ret = pah_err_unknown; debug_printf("_pah8011_shutdown() \n"); ret = pah_8011_shutdown(g_state.flags.powerdown_int_status); if (PAH_FAILED(ret)) goto FAIL; // reset states g_state.has_started_tg = 0; g_state.has_started_ppg = 0; g_state.has_started_touch = 0; // reset properties g_state.prop_curr.report_sample_num_per_ch = 0; return true; FAIL: debug_printf("_pah8011_shutdown error \n"); return false; } static bool _pah8011_startup(void) { pah_ret ret = pah_err_unknown; debug_printf("_pah8011_startup() \n"); ret = pah_8011_startup(); if (PAH_FAILED(ret)) goto FAIL; return true; FAIL: debug_printf("_pah8011_startup error \n"); return false; } static bool _pah8011_start_tg(void) { uint8_t data = 0; if (g_state.has_started_tg) return true; debug_printf("_pah8011_start_tg() \n"); // must sleep between update flag and TG enable. delay_ms(1); // mask if (!pah_comm_write(0x7f, 0x01)) goto FAIL; if (!pah_comm_read(0x37, &data)) //mask goto FAIL; PAH_CLEAR_BIT(data, 0); // IntSramFIFO_MaskInt PAH_CLEAR_BIT(data, 2); // int_sramfifo_overflow_mask PAH_CLEAR_BIT(data, 3); // int_sramfifo_underflow_mask if (!pah_comm_write(0x37, data)) goto FAIL; // TG enable if (!pah_comm_write(0x30, 0x01)) //TG enable goto FAIL; // wait for clk delay_ms(1); // clear int_req if (!pah_comm_write(0x7F, 0x02)) //Bank 2 goto FAIL; if (!pah_comm_write(0x1B, 0xFF)) //int_req_array goto FAIL; // mask if (!pah_comm_write(0x7f, 0x01)) goto FAIL; if (!pah_comm_read(0x37, &data)) //mask goto FAIL; PAH_CLEAR_BIT(data, 4); // AllInt_MaskInt if (!pah_comm_write(0x37, data)) goto FAIL; g_state.has_started_tg = 1; return true; FAIL: debug_printf("_pah8011_start_tg error \n"); return false; } static bool _pah8011_start_ppg(void) { uint8_t data = 0; uint8_t ppg_ch_enabled[pah8011_ppg_ch_num]; if (g_state.has_started_ppg) return true; debug_printf("_pah8011_start_ppg() \n"); pah8011_get_ppg_ch_enabled(ppg_ch_enabled); if (!pah_comm_write(0x7F, 0x01)) //bank1 goto FAIL; //channel a if (ppg_ch_enabled[pah8011_ppg_ch_a]) { if (!pah_comm_read(0x21, &data)) goto FAIL; PAH_SET_BIT(data, 0); if (!pah_comm_write(0x21, data)) goto FAIL; } //channel b if (ppg_ch_enabled[pah8011_ppg_ch_b]) { if (!pah_comm_read(0x23, &data)) goto FAIL; PAH_SET_BIT(data, 0); if (!pah_comm_write(0x23, data)) goto FAIL; } //channel c if (ppg_ch_enabled[pah8011_ppg_ch_c]) { if (!pah_comm_read(0x25, &data)) goto FAIL; PAH_SET_BIT(data, 0); if (!pah_comm_write(0x25, data)) goto FAIL; } g_state.has_started_ppg = 1; return true; FAIL: debug_printf("_pah8011_start_ppg error \n"); return false; } static bool _pah8011_stop_ppg(void) { uint8_t data = 0; if (!g_state.has_started_ppg) return true; debug_printf("_pah8011_stop_ppg() \n"); if (!pah_comm_write(0x7F, 0x01)) //bank1 goto FAIL; //channel a if (!pah_comm_read(0x21, &data)) goto FAIL; PAH_CLEAR_BIT(data, 0); if (!pah_comm_write(0x21, data)) goto FAIL; //channel b if (!pah_comm_read(0x23, &data)) goto FAIL; PAH_CLEAR_BIT(data, 0); if (!pah_comm_write(0x23, data)) goto FAIL; //channel c if (!pah_comm_read(0x25, &data)) goto FAIL; PAH_CLEAR_BIT(data, 0); if (!pah_comm_write(0x25, data)) goto FAIL; g_state.has_started_ppg = 0; return true; FAIL: debug_printf("_pah8011_stop_ppg error \n"); return false; } static bool _pah8011_start_touch(void) { uint8_t data = 0; if (g_state.has_started_touch) return true; debug_printf("_pah8011_start_touch() \n"); if (!pah_comm_write(0x7F, 0x01)) //bank1 goto FAIL; // 0x36 if (!pah_comm_read(0x36, &data)) goto FAIL; if (g_state.prop_curr.is_int2_as_touch_flag) { PAH_CLEAR_BIT(data, 0); PAH_SET_BIT(data, 1); } else { PAH_SET_BIT(data, 0); PAH_CLEAR_BIT(data, 1); } if (!pah_comm_write(0x36, data)) goto FAIL; // 0x37 if (!pah_comm_read(0x37, &data)) goto FAIL; if (g_state.prop_curr.is_int2_as_touch_flag) { PAH_SET_BIT(data, 1); } else { PAH_CLEAR_BIT(data, 1); } if (!pah_comm_write(0x37, data)) goto FAIL; g_state.has_started_touch = 1; return true; FAIL: debug_printf("_pah8011_start_touch error \n"); return false; } static bool _pah8011_stop_touch(void) { uint8_t data = 0; if (!g_state.has_started_touch) return true; debug_printf("_pah8011_stop_touch() \n"); if (!pah_comm_write(0x7F, 0x01)) //bank1 goto FAIL; // 0x36 if (!pah_comm_read(0x36, &data)) goto FAIL; PAH_SET_BIT(data, 0); PAH_CLEAR_BIT(data, 1); if (!pah_comm_write(0x36, data)) goto FAIL; // 0x37 if (!pah_comm_read(0x37, &data)) goto FAIL; if (g_state.prop_curr.is_int2_as_touch_flag) { // empty } else { PAH_SET_BIT(data, 1); } if (!pah_comm_write(0x37, data)) goto FAIL; g_state.has_started_touch = 0; return true; FAIL: debug_printf("_pah8011_stop_touch error \n"); return false; } static bool _pah8011_clear_fifo_and_int_req(void) { pah_ret ret = pah_err_unknown; uint8_t int_req = 0; debug_printf("_pah8011_clear_fifo_and_int_req() \n"); if (!pah_comm_write(0x7F, 0x01)) //bank1 goto FAIL; // clear FIFO if (!pah_comm_write(0x70, 0x01)) // clear FIFO goto FAIL; ret = pah_8011_update_flag(); if (PAH_FAILED(ret)) goto FAIL; delay_ms(PPG_FRAME_INTERVAL_MS); if (!pah_comm_write(0x70, 0x00)) // clear FIFO goto FAIL; ret = pah_8011_update_flag(); if (PAH_FAILED(ret)) goto FAIL; //clear interrupt if (!pah_comm_write(0x7F, 0x02)) //Bank 2 goto FAIL; if (!pah_comm_read(0x1B, &int_req)) //read interrupt status goto FAIL; if (int_req > 0) { if (!pah_comm_write(0x1B, int_req)) //clear interrupt goto FAIL; } return true; FAIL: debug_printf("_pah8011_clear_fifo_and_int_req() error \n"); return false; } static bool _pah8011_update_report_num(void) { if (g_state.prop_curr.report_sample_num_per_ch != g_state.prop_next.report_sample_num_per_ch) { debug_printf_2("_pah8011_update_report_num(). report_sample_num_per_ch %d -> %d \n", g_state.prop_curr.report_sample_num_per_ch, g_state.prop_next.report_sample_num_per_ch); if (!_pah8011_set_report_num(g_state.prop_next.report_sample_num_per_ch * pah_get_fifo_ch_num())) goto FAIL; g_state.prop_curr.report_sample_num_per_ch = g_state.prop_next.report_sample_num_per_ch; } return true; FAIL: debug_printf("_pah8011_update_report_num() fail \n"); return false; } static bool _pah8011_set_report_num(uint32_t samples_per_read) { pah_ret ret = pah_err_unknown; uint8_t data = 0; uint32_t samples_per_read_plus1 = samples_per_read + 1; debug_printf_1("_pah8011_set_report_num(). samples_per_read_plus1 = %d \n", samples_per_read_plus1); if (!pah_comm_write(0x7F, 0x01)) //bank1 goto FAIL; //Rpt num. write MSB first. data = (((samples_per_read_plus1) >> 8) & 0x01); if (!pah_comm_write(0x57, data)) goto FAIL; //Rpt num. write LSB last. data = ((samples_per_read_plus1)& 0xFF); if (!pah_comm_write(0x56, data)) goto FAIL; ret = pah_8011_update_flag(); if (PAH_FAILED(ret)) goto FAIL; return true; FAIL: debug_printf("_pah8011_set_report_num() fail \n"); return false; } static bool _pah8011_turn_led(bool on) { debug_printf_1(">>>> _pah8011_turn_led(), on = %d \n", on); if (!pah_comm_write(0x7F, 0x00)) //bank0 goto FAIL; if (on) { if (!pah_comm_write(0x70, 0x01)) goto FAIL; if (!pah_comm_write(0x71, 0x02)) goto FAIL; if (!pah_comm_write(0x72, 0x04)) goto FAIL; } else { if (!pah_comm_write(0x70, 0x00)) goto FAIL; if (!pah_comm_write(0x71, 0x00)) goto FAIL; if (!pah_comm_write(0x72, 0x00)) goto FAIL; } debug_printf("<<<< _pah8011_turn_led() \n"); return true; FAIL: debug_printf("_pah8011_turn_led error \n"); return false; } #if defined(ENABLE_FIFO_CHECKSUM) static bool _pah8011_cks(uint8_t *fifo, uint32_t samples_read, uint32_t cks_read) { uint32_t *s = (uint32_t *)fifo; uint32_t raw; uint32_t cks_cal = 0; uint32_t i; //checksum compare for (i = 0; i < samples_read; i++) { raw = *(s); cks_cal = cks_cal ^ raw; s++; } if (cks_cal != cks_read) { s = (uint32_t *)fifo; debug_printf("fifo = { \n"); for (i = 0; i < samples_read; i++) { raw = *(s); debug_printf_1("%X, ", raw); s++; } debug_printf("} \n"); debug_printf_2("cks_cal == %X, cks_read == %X \n", cks_cal, cks_read); return false; } return true; } #endif // ENABLE_FIFO_CHECKSUM static bool _pah8011_read_touch_flag(uint8_t *touch_flag) { uint8_t data = 0; if (!touch_flag) goto FAIL; if (!pah_comm_write(0x7F, 0x02)) //Bank 2 goto FAIL; if (!pah_comm_read(0x00, &data)) goto FAIL; *touch_flag = data & 0x01; return true; FAIL: debug_printf("_pah8011_read_touch_flag() fail \n"); return false; } static bool _pah8011_enable_fifo_clk(bool enable) { if (enable) { if (!pah_comm_write(0x7F, 0x01)) //Bank 1 goto FAIL; if (!pah_comm_write(0x71, 0x00)) goto FAIL; if (!pah_comm_write(0x7F, 0x00)) //Bank 0 goto FAIL; if (!pah_comm_write(0x10, 0x03)) goto FAIL; } else // disable { if (!pah_comm_write(0x7F, 0x01)) //Bank 1 goto FAIL; if (!pah_comm_write(0x71, 0x01)) goto FAIL; if (!pah_comm_write(0x7F, 0x00)) //Bank 0 goto FAIL; if (!pah_comm_write(0x10, 0x00)) goto FAIL; } return true; FAIL: debug_printf("_pah8011_enable_fifo_clk error \n"); return false; } static pah_ret _pah8011_read_ppg_fifo(uint32_t samples_per_read_per_ch, uint32_t ch_num) { pah_ret ret = pah_err_unknown; uint32_t samples_per_read = samples_per_read_per_ch * ch_num; //debug_printf_1("_pah8011_read_ppg_fifo(). samples_per_read = %d \n", samples_per_read); // read fifo if (!pah_comm_write(0x7F, 0x03)) //Bank 3 { ret = pah_err_platform_fail; goto FAIL; } else if (!pah_comm_burst_read(0, g_state.fifo_data, samples_per_read * 4)) //read FIFO { ret = pah_err_platform_fail; goto FAIL; } else { uint32_t *s = (uint32_t *)g_state.fifo_data; // update touch_flag if (g_state.fifo_data_contain_touch_bit) { g_state.touch_flag = s[(samples_per_read - 1)] & 0x01; } else { if (!_pah8011_read_touch_flag(&g_state.touch_flag)) { ret = pah_err_platform_fail; goto FAIL; } } #if defined(ENABLE_FIFO_CHECKSUM) { uint8_t cks[8] = { 0 }; if (!pah_comm_write(0x7F, 0x02)) { ret = pah_err_platform_fail; goto FAIL; } if (!pah_comm_burst_read(0x1C, cks, 4)) //checksum { ret = pah_err_platform_fail; goto FAIL; } if (!_pah8011_cks(g_state.fifo_data, samples_per_read, *(uint32_t *)cks)) { debug_printf("_pah8011_read_ppg_fifo(). _pah8011_cks fail\n"); ret = pah_err_fifo_checksum_fail; goto FAIL; } debug_printf("_pah8011_read_ppg_fifo(). _pah8011_cks success\n"); } #endif // ENABLE_FIFO_CHECKSUM // report fifo if (g_state.fp_report_fifo_handler) { pah_report_fifo fifo; fifo.data = g_state.fifo_data; fifo.touch_flag = g_state.touch_flag; g_state.fp_report_fifo_handler(g_state.user_data, &fifo); } g_state.fifo_data_num_per_ch = samples_per_read_per_ch; } return pah_success; FAIL: debug_printf_1("_pah8011_read_ppg_fifo(). fail, ret = %d \n", ret); return ret; } static pah_ret _pah8011_task_dri(uint8_t int_req, uint32_t ch_num) { pah_ret ret = pah_err_unknown; // no interrupt if (!int_req) { // ignore debug_printf("_pah8011_task_dri(). No interrupt. \n"); return pah_pending; } // fifo interrupt if (int_req & 0x01) { uint32_t samples_per_read = g_state.prop_curr.report_sample_num_per_ch * ch_num; uint16_t fifo_data_number = 0; if (!pah_comm_write(0x7F, 0x02)) //Bank 2 { ret = pah_err_platform_fail; goto FAIL; } if (!pah_comm_burst_read(0x25, (uint8_t*)&fifo_data_number, 2)) { ret = pah_err_platform_fail; goto FAIL; } //debug_printf("_pah8011_task_dri(). fifo_data_number = %d, samples_per_read = %d. \n", fifo_data_number, samples_per_read); if (fifo_data_number < samples_per_read) return pah_pending; ret = _pah8011_read_ppg_fifo(g_state.prop_curr.report_sample_num_per_ch, ch_num); if (ret != pah_success) goto FAIL; } // touch interrupt if (int_req & 0x02) { if (!_pah8011_read_touch_flag(&g_state.touch_flag)) { ret = pah_err_platform_fail; goto FAIL; } } return pah_success; FAIL: debug_printf_1("_pah8011_task_dri(). fail, ret = %d \n", ret); return ret; } static pah_ret _pah8011_task_polling(uint8_t int_req, uint32_t ch_num) { pah_ret ret = pah_err_unknown; uint16_t fifo_data_number = 0; uint32_t samples_per_read_per_ch = 0; if (!pah_comm_write(0x7F, 0x02)) //Bank 2 { ret = pah_err_platform_fail; goto FAIL; } if (!pah_comm_burst_read(0x25, (uint8_t*)&fifo_data_number, 2)) { ret = pah_err_platform_fail; goto FAIL; } if (fifo_data_number == 0) return pah_pending; debug_printf_1("_pah8011_task_polling(). fifo_data_number = %d \n", fifo_data_number); samples_per_read_per_ch = (fifo_data_number - 1) / ch_num; if (samples_per_read_per_ch < 1) return pah_pending; // enable clk if (!_pah8011_enable_fifo_clk(true)) { ret = pah_err_platform_fail; goto FAIL; } ret = _pah8011_read_ppg_fifo(samples_per_read_per_ch, ch_num); // disable clk if (!_pah8011_enable_fifo_clk(false)) { ret = pah_err_platform_fail; goto FAIL; } if (ret != pah_success) goto FAIL; return pah_success; FAIL: debug_printf_1("_pah8011_task_polling(). fail, ret = %d \n", ret); return ret; }